• 検索結果がありません。

テスト駆動開発入門 ネクストステップ

N/A
N/A
Protected

Academic year: 2021

シェア "テスト駆動開発入門 ネクストステップ"

Copied!
70
0
0

読み込み中.... (全文を見る)

全文

(1)

テスト駆動開発入門

ネクストステップ

(2)

謝辞

• 主催の今給黎さん

• 和田さん、会場提供、スタッフの方々

• 参加者の皆さま

(3)

自己紹介

• 井芹 洋輝(@goyoki/id:goyoki)

• 組み込みエンジニア

• WACATE実行委員/TDD研究会

• 講演/執筆:

– XP祭り関西「ユニットテストの保守性を作りこむ」 – Androidテスト祭り「テストの活用による開発効率化」 – 並カン「FPGA/HDLを活用したソフトウェア並列処理の構築」等

(4)

概要

本講義はTDDの基本サイクルを学んだ方

が対象です。

本講義では

TDDを開発で実践するための

知識

TDDについて自立して学習を進め

るための知識

を学び、一人前のTDD使い

へのスタートアップを手助けします。

(5)

概要

テストを 整える 変更に 備える 変更に 対処する

TDD実践のネクストステップ

TDD学習のネクストステップ

基礎を 身につける より 活用する 応用分野 を学ぶ

(6)

実践のネクストステップ

テストを 整える 変更に 備える 変更に 対処する

TDD実践のネクストステップ

TDD学習のネクストステップ

基礎を 身につける より 活用する 応用分野 を学ぶ

(7)

実践のネクストステップ

TDDを継続していくと、TDDの基本サイク

ルに

テストの再利用と変更

のタスクが加わり

ます。それらはしばしばTDDの効率を左右

するため工夫や対策が必要です。

(8)

テストを整える

テストを 整える 変更に 備える 変更を 対処する

TDD実践のネクストステップ

TDD学習のネクストステップ

基礎を 身につける より 活用する 応用分野 を学ぶ

(9)

テストを整える

TDDであってもテスト設計を見直し、テスト

に穴がないか、これまでの作業が適切だった

かチェックする必要があります。

不適切なテ

ストは実装ミス・リファクタリングでのデグレー

ドを見逃し、テストの再利用を阻害する

リス

クを持っています。

(10)

テストを整える

テストの網羅度をチェック

• 仕様ベースの網羅

– テストが仕様を網羅しているか – 仕様ベースのテスト設計等

• 構造ベースの網羅

– テストがコードを網羅しているか – コードカバレッジ等 4で割り切れる N Y Y Y 100で割り切れる N N Y Y 400で割り切れる N N N Y うるうどし N Y N Y //うるう年か判定する

bool isLeapYear(unsigned int year) {

if (year % 400 == 0) {

return true; }

if ((year % 4 == 0) && (year % 100 != 0)) {

return true; }

return false; }

(11)

仕様ベースの網羅

ex)同値分割法によるチェック

• 出力やふるまいで同じように扱えるグループに、入

出力をグルーピングする

(12)

仕様ベースの網羅

ex)同値分割法によるチェック

• 「6歳未満は無料。6歳以上12歳以下は半額。

13歳以上は定額」

(13)

仕様ベースの網羅

ex)同値分割法によるチェック

• 「6歳未満は無料。6歳以上12歳以下は半額。

13歳以上は定額」

– 出力をグルーピング

• 無料 • 半額 • 定額

(14)

仕様ベースの網羅

ex)同値分割法によるチェック

• 「6歳未満は無料。6歳以上12歳以下は半額。

13歳以上は定額」

– 入力もグルーピングし、入出力のグループを抽出

0 6 12 -∞ +∞ ありえない 無料 半額 定額 年齢 4つにグルーピング

(15)

仕様ベースの網羅

ex)同値分割法によるチェック

• 「6歳未満は無料。6歳以上12歳以下は半額。

13歳以上は定額」

– グループごとに代表値を決めてテストの入力値を抽出

0 6 12 -∞ +∞ ありえない 無料 半額 定額 代表値-1 代表値0 代表値5 代表値6 代表値12 代表値13 代表値をテストの入力に指定

(16)

仕様ベースの網羅

ex)同値分割法によるチェック

TEST(HogeTest, Invalid) { EXPECT_EQ(…, checkFee(-1)) } TEST(HogeTest, Free) { EXPECT_EQ(…, checkFee(0)) EXPECT_EQ(…, checkFee(5) } TEST(HogeTest, Half) { EXPECT_EQ(…, checkFee(6)) EXPECT_EQ(…, checkFee(12)) } TEST(HogeTest, Full) { EXPECT_EQ(…, checkFee(13)) }

テストコードがグループや代表

値を網羅しているかチェック

穴があれば埋める

あるいは

最初から意識してテストを書く

(17)

構造ベースの網羅

• コードカバレッジを用いる

• テストが妥当なブランチカバレッジやループカバレッ

ジの網羅性を持つことをチェック

(18)

変更に備える

テストを 整える 変更に 備える 変更に 対処する

TDD実践のネクストステップ

TDD学習のネクストステップ

基礎を 身につける より 活用する 応用分野 を学ぶ

(19)

変更に備える

TDDではテストをリファクタリングや自動回

帰テストとして再利用するため、

テストに保

守性が要求されます

柔軟な開発を支えるためにも、

プロダクト/テ

ストを区別せずコードを洗練させる必要が

あります

(20)

変更に備える

テストは変更を支える砦となりえますが、同

時にテストは変更の障害ともなりえます。

テストコードも保守困難なレガシーコードと

なります

(21)

変更に備える

• 読みやすくする

• 危ないコードを分離する

• 重複をなくす

(22)

変更に備える

• 読みやすくする

• 危ないコードを分離する

• 重複をなくす

(23)

読みやすくする

• 何をテストしているのかわかりやすい

• 変更箇所の特定が楽

• 変更ミスを防げる

(24)

読みやすくする

TEST(HogeTest, Test1)

{

}

TEST(testHoge, Test2)

{

}

(25)

読みやすくする

TEST(HogeTest, Test1)

{

}

TEST(testHoge, Test2)

{

}

なんのテストかわからない

(26)

読みやすくする

TEST(HogeTest, commandInputInvalidError)

{

}

TEST(HogeTest, commandInputBOFError)

{

}

適切な名前を与える

(27)

読みやすくする

TEST(HogeTest, Fuga) { MotorStatus motorStatus(133, 232); InspectionFuga inspector; inspector.set(createMaintenanceInfo(motorStatus); EXPECT_EQ(START, inspector.getState()); EXPECT_EQ(true, inspector.isEmpty()); inspector.initialize(); EXPECT_EQ(INFO, inspector.getState()); EXPECT_EQ(false, inspector.isEmpty()); }

(28)

読みやすくする

TEST(HogeTest, Fuga) { MotorStatus motorStatus(133, 232); InspectionFuga inspector; inspector.set(createMaintenanceInfo(motorStatus); EXPECT_EQ(START, inspector.getState()); EXPECT_EQ(true, inspector.isEmpty()); inspector.initialize(); EXPECT_EQ(INFO, inspector.getState()); EXPECT_EQ(false, inspector.isEmpty()); }

何をテストしているかが散漫

テストのバグをみつけにくい

(29)

読みやすくする

TEST(HogeTest, FugaConstractor) {

InspectionFuga inspector = createInspectionFugaDummy();

EXPECT_EQ(START, inspector.getState()); EXPECT_EQ(true, inspector.isEmpty());

}

TEST(HogeTest, FugaInitialize) {

InspectionFuga inspector = createInspectionFugaDummy(); inspector.initialize();

EXPECT_EQ(INFO, inspector.getState()); EXPECT_EQ(false, inspector.isEmpty());

(30)

変更に備える

• 読みやすくする

• 危ないコードを分離する

• 重複をなくす

(31)

危ないコードを分離する

TEST(FooTest, Bar) { MotorStatus motorStatus(0, 0); MaintenanceData mtData; MaintenanceType mtType(createRegionID(EU)); setInitialData(mtData, mtType); … InspectionFuga inspector;

inspector.set(MaintenanceInfo(mtData, mtType), motorStatus); EXPECT_EQ(inspector…)

(32)

危ないコードを分離する

TEST(FooTest, Bar) { MotorStatus motorStatus(0, 0); MaintenanceData mtData; MaintenanceType mtType(createRegionID(EU)); setInitialData(mtData, mtType); … InspectionFuga inspector;

inspector.set(MaintenanceInfo(mtData, mtType), motorStatus); … }

プロダクトコードに過依存

その他:

変更リスクの高いコード

堅牢性の劣るコード

(33)

危ないコードを分離する

[テスト側でラッピング]

TEST(FooTest, Bar) {

InspectionFuga inspector = CreateInspectionFuga(0, EU);

inspector.set(MaintenanceInfo(mtData, mtType), motorStatus); …

(34)

危ないコードを分離する

[プロダクト側のインターフェースを改善]

TEST(FooTest, Bar) {

InspectionFuga inspector(0, 0, EU);

inspector.set(MaintenanceInfo(mtData, mtType), motorStatus); …

(35)

変更に備える

• 読みやすくする

• 危ないコードを分離する

• 重複をなくす

(36)

重複をなくす

[Test Utility Method]

TEST_F(BuyerTest, addSameStatus) {

Buyer buyer;

Customer customer1("Taro", "Yamada", 15, 2, "HOGE|FUGA"); customer1.addCategory(STATE_ACTIVE);

Customer customer2("Taro", "Yamada", 15, 2, "HOGE|FUGA|HOGEHOGE"); customer2.addCategory(STATE_ACTIVE); …. buyer.add(customer1); buyer.add(customer2); …. EXPECT_EQ(0, buyer.getSection()); }

(37)

重複をなくす

[Test Utility Method]

Customer createCustomer(string status) {

Customer customer("Taro", "Yamada", 15, 2, status); customer.addCategory(STATE_ACTIVE);

return customer;

} Parameterized Creation Method TEST_F(BuyerTest, addSameStatus)

{

Buyer buyer;

Customer customer1 = createCustomer("HOGE|FUGA");

Customer customer2 = createCustomer("HOGE|FUGA|HOGEHOGE"); … buyer.add(customer1); buyer.add(customer2); … EXPECT_EQ(0, buyer.getSection()); }

(38)

重複をなくす

[Parameterized Test]

TEST_P(HogeTest, InvalidValueMinus) { Hoge hoge(-1); EXPECT_EQ(0, hoge.size()); } TEST_P(HogeTest, InvalidValueZero) { Hoge hoge(0); EXPECT_EQ(0, hoge.size()); } TEST_P(HogeTest, InvalidValueTooBig) { Hoge hoge(124566); EXPECT_EQ(0, hoge.size()); } …

(39)

重複をなくす

[Parameterized Test]

class HogeTest : public testing::TestWithParam<int> {}; INSTANTIATE_TEST_CASE_P(InvalidValueInstance, HogeTest, testing::Values(-1, 0, 124566)); TEST_P(HogeTest, hogehoge) { Hoge hoge(GetParam()); EXPECT_EQ(0, hoge.size()); } Parameterized Test

(40)

変更に備える

• 読みやすくする

• 危ないコードを分離する

• 重複をなくす

(41)

影響範囲を限定する/副作用をなくす

Foo foo; TEST_F(HogeTest, Fuga) { … } TEST_F(HogeTest, Piyo) { … }

(42)

影響範囲を限定する/副作用をなくす

TEST_F(HogeTest, Fuga) { Foo foo; … } TEST_F(HogeTest, Piyo) { Foo foo; … } ローカル変数にする テストクラスのメンバにする

(43)

影響範囲を限定する/副作用をなくす

Void SetUp() { 外部コンポーネントの初期状態を記録する } TEST_F(Buyer, test_add_sameStatus) { 外部コンポーネントを使ってテスト …. } Void TearDown() { 外部コンポーネントを初期状態にロールバックする } 構造的にも時間軸的にも独立させる 他のテストコードを変更しても結果が変わらない 順序を変えても、どのようなタイミングでも結果が変わらない

(44)

テストを整える&変更に備える

実施タイミング

TDDではプロダクト/テストを区別せずコード

を洗練させていくべきです

テストコードであっても良いコードを目指すべ

きですし、プロダクトコードのリファクタリングと

同じ扱いで設計改善すべきです。

(45)

テストを整える&変更に備える

実施タイミング

Assertファースト による追加・変更 (RED→GREEN) リファクタリング (Refactor) Green

RED

GREEN

REFACT

OR

(46)

テストを整える&変更に備える

実施タイミング

リファクタリング (Refactor[PRODUCT]) テストを整える

RED

GREEN

テストを 整える REFACTOR • TEST • PRODUCT Green Assertファースト による追加・変更 (RED→GREEN) テストコードの 設計改善 (REFACTOR[TEST])

(47)

変更に対処する

テストを 整える 変更に 備える 変更に 対処する

TDD実践のネクストステップ

TDD学習のネクストステップ

基礎を 身につける より 活用する 応用分野 を学ぶ

(48)

変更

TDDでは開発の進展、リファクタリング、仕

様変更などによりしばしばプロダクトコードの

変更が発生します。

TDDではプロダクトコードに依存するテスト

が早期から作られるため、

テストを以下に効

率よく変更に対応させるかが効率確保の鍵

となりえます

(49)

変更に対処する

1. よく考える

(1.5. 変更を受け入れられるように設計改善)

2. RED

3. GREEN

4. REFACTOR

(50)

変更に対処する

TEST(…) { TestTarget target(0); … } TEST(…) { TestTarget target(1); … } …. Class TestTarget {

void TestTarget(int hoge) { ….

} }

(51)

変更に対処する

TestTarget(int hoge) からTestTarget(int hoge, int fuga)に変更。 Int fugaに応じて複雑な処理を・・・ TEST(…) { TestTarget target(0); … } TEST(…) { TestTarget target(1); … } …. Class TestTarget {

void TestTarget(int hoge) { ….

} }

(52)

変更に対処する

TEST(…) { TestTarget target(0); … } TEST(…) { TestTarget target(1); … } TEST(…) { TestTarget target(2); … } …. Class TestTarget {

void TestTarget(int hoge) { ….

} }

TestTarget(int hoge) からTestTarget(int hoge, int fuga)に変更。 Int fugaに応じて複雑な処理を・・・

よく考える

無理のない小さなステップで

効率よく変更できるように

(53)

変更に対処する[1]

Parallel Change

TEST(…) { TestTarget target(0); … } TEST(…) { TestTarget target(1); … } TEST(…) { TestTarget target(2); … } …. Class TestTarget {

void TestTarget(int hoge) { ….

}

void TestTarget(int hoge, int fuga) { …

} }

TestTarget(int hoge) からTestTarget(int hoge, int fuga)に変更。 Int fugaに応じて複雑な処理を・・・

新旧共存でTDD

(54)

変更に対処する[1]

Parallel Change

TEST(…) { TestTarget target(0, 0); … } TEST(…) { TestTarget target(1); … } TEST(…) { TestTarget target(2); … } …. Class TestTarget {

void TestTarget(int hoge) { ….

}

void TestTarget(int hoge, int fuga) { …

} }

TestTarget(int hoge) からTestTarget(int hoge, int fuga)に変更。 Int fugaに応じて複雑な処理を・・・

新旧共存でTDD

(55)

変更に対処する[2]

TDDのための事前変更

TEST(…) { TestTarget target(0, 0); … } TEST(…) { TestTarget target(1, 0); … } TEST(…) { TestTarget target(2, 0); … } …. Class TestTarget {

void TestTarget(int hoge, int fuga) { ….

} }

TestTarget(int hoge) からTestTarget(int hoge, int fuga)に変更。 Int fugaに応じて複雑な処理を・・・

Dummyで置き換えつつ

インターフェースを変更

(56)

変更に対処する

• 無理のない小さなステップで、効率よく変更でき

るように考える

– 事前対策 – Pallalel Changeや前倒しのインターフェース変更等

• テストの保護を壊さない

変更にもテストで戦う

(57)

学習のネクストステップ

テストを 整える 変更に 備える 変更に 対処する

TDD実践のネクストステップ

TDD学習のネクストステップ

基礎を 身につける より 活用する 応用分野 を学ぶ

(58)

TDDを学ぶ

TDDは

文献

情報発信源

コミュニティ

ら学ぶことができます。

TDDはシンプルな開発手法ですが、様々

な関連分野、応用分野とリンクしているた

め、勉強の余地を大いに持っています。

(59)

基礎を身につける

テストを 整える 変更に 備える 変更を 対処する

TDD実践のネクストステップ

TDD学習のネクストステップ

基礎を 身につける より 活用する 応用分野 を学ぶ

(60)

基礎を身につける

@t_wada Id:t-wada

(61)

より活用する

テストを 整える 変更に 備える 変更に 対処する

TDD実践のネクストステップ

TDD学習のネクストステップ

基礎を 身につける より 活用する 応用分野 を学ぶ

(62)

より活用する

(63)

より活用する

テストコードの実装

xutp magagine

ぺけま

Coming soon!

xUnit Test Patterns読書会Wiki http://www.fieldnotes.jp/xutp/

Id:setoazusa @setoazusa

(64)

より活用する

(65)

応用分野を学ぶ

テストを 整える 変更に 備える 変更に 対処する

TDD実践のネクストステップ

TDD学習のネクストステップ

基礎を 身につける より 活用する 応用分野 を学ぶ

(66)

応用分野を学ぶ

テストの活用×TDD

WACATE(もうすぐ募集開始!) http://wacate.jp/

Testing Engineer's Forum

http://www.swtest.jp/wiki/index.php?swte st.jp/wiki/forum

(67)

応用分野を学ぶ

DVCS×TDD

SCM Boot camp http://d.hatena.ne.jp/kyon_mm/archive?word=*%5Bscmbc%5D Id:bleis-tift @bleis Id:kyon_mm @kyon_mm Id:pocketberserker @pocketberserker

(68)

応用分野を学ぶ

BDD/Outside-In TDD

Growing Object-Oriented Software, Guided by Tests(goos)読書会

http://devtesting.jp/goos/

Id:setoazusa @setoazusa

(69)

その他

TDDBC運営コミュニティ

TDDBC

(70)

ご清聴ありがとうございました

テストを 整える 変更に 備える 変更に 対処する

TDD実践のネクストステップ

TDD学習のネクストステップ

基礎を 身につける より 活用する 応用分野 を学ぶ

参照

Outline

関連したドキュメント

1、研究の目的 本研究の目的は、開発教育の主体形成の理論的構造を明らかにし、今日の日本における

基本計画は、基本構想で定めるめざすまちの姿と 5 つの基本目標を実現するため、12 年間(平 成 28 年度~平成

研究開発活動の状況につきましては、新型コロナウイルス感染症に対する治療薬、ワクチンの研究開発を最優先で

諸君はこのような時代に大学に入学されました。4年間を本

第四。政治上の民本主義。自己が自己を統治することは、すべての人の権利である

Freund, Dual gauge programs, with applications to quadratic programming and the minimum-norm problem,. Mathematical

目的 これから重機を導入して自伐型林業 を始めていく方を対象に、基本的な 重機操作から作業道を開設して行け

、コメント1点、あとは、期末の小 論文で 70 点とします(「全て持ち込 み可」の小論文式で、①最も印象に 残った講義の要約 10 点、②最も印象 に残った Q&amp;R 要約