Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
テスト駆動開発入門ハンズオン/TDD HandsOn
Search
Hiroki Iseri
June 11, 2011
Programming
300
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
テスト駆動開発入門ハンズオン/TDD HandsOn
Hiroki Iseri
June 11, 2011
More Decks by Hiroki Iseri
See All by Hiroki Iseri
生成AI活用でQAエンジニアにどのような仕事が生まれるか/Support Required of QA Engineers for Generative AI
goyoki
1
690
開発に寄りそう自動テストの実現
goyoki
3
4.2k
自動テストを活かすためのテスト分析・テスト設計の進め方/JaSST25 Shikoku
goyoki
3
2.8k
チームのテスト力を総合的に鍛えてシフトレフトを推進する/Shifting Left with Software Testing Improvements
goyoki
6
5k
チームのテスト力を鍛える
goyoki
4
3.5k
ソフトウェアテスト徹底指南書の紹介
goyoki
1
2.9k
プロダクト開発を成功させるためのソフトウェア品質保証のアプローチと技術/Software QA Approach for Puduct Success
goyoki
2
2.2k
チームのテスト力を総合的に鍛えて品質、スピード、レジリエンスを共立させる/Testing approach that improves quality, speed, and resilience
goyoki
5
3.7k
テスト分析入門/Test Analysis Tutorial
goyoki
12
6.9k
Other Decks in Programming
See All in Programming
使用 Meilisearch 建立新聞搜尋工具
johnroyer
0
170
SREは、MCPとSRE Agentをこう使え!
kazumax55
0
150
共通化で考えるべきは、実装より公開する型だった
codeegg
0
270
jsmini JavaScript Engine を作ってみた話
yosuke_furukawa
PRO
0
160
変わらないものが、変わるものを決める — 意図駆動開発 × イベントソーシング × イミュータブル | What Doesn't Change Decides What Can — IDD × Event Sourcing × Immutability
tomohisa
0
140
Welcome to the "Parametricity" 🏙️ − Generic だけど Specific な世界 −
guvalif
PRO
1
180
2年かけて Deno に DOMMatrix を実装した話 / How I implemented DOMMatrix in Deno over two years
petamoriken
0
110
鹿野さんに聞く!『TypeScriptコードレシピ集』で磨く実践力
tonkotsuboy_com
4
1.1k
はてなアカウント基盤 State of the Union
cockscomb
1
1.3k
言語を使う側から、作る側へ。 自作 Lisp で得た新たな気づき。
andpad
0
130
GitHubCopilotCLIのスラッシュコマンドを自作してみる
htkym
0
100
なぜ関数型プログラミングで「型」と「証明」が語られるのか #fp_matsuri
kajitack
3
1k
Featured
See All Featured
Imperfection Machines: The Place of Print at Facebook
scottboms
270
14k
How to build an LLM SEO readiness audit: a practical framework
nmsamuel
1
810
A Tale of Four Properties
chriscoyier
163
24k
brightonSEO & MeasureFest 2025 - Christian Goodrich - Winning strategies for Black Friday CRO & PPC
cargoodrich
3
750
How To Speak Unicorn (iThemes Webinar)
marktimemedia
1
510
Measuring & Analyzing Core Web Vitals
bluesmoon
9
900
Agile that works and the tools we love
rasmusluckow
331
22k
The innovator’s Mindset - Leading Through an Era of Exponential Change - McGill University 2025
jdejongh
PRO
1
220
Deep Space Network (abreviated)
tonyrice
0
230
コードの90%をAIが書く世界で何が待っているのか / What awaits us in a world where 90% of the code is written by AI
rkaga
62
45k
Gemini Prompt Engineering: Practical Techniques for Tangible AI Outcomes
mfonobong
2
470
Writing Fast Ruby
sferik
630
63k
Transcript
テスト駆動開発入門 ハンズオン講座(前篇) 2011/6/11 井芹
目的 • TDDの具体的なイメージをつかんでもらう – 実際の進め方 – 運用時に必要性の高い周辺知識 – TDDのテストの特徴
概要(前篇) • イントロダクション • ハンズオン課題1 • TDDの概要 – 定義/手順/利益など •
ハンズオン課題2 • ソフトウェアテストとしてのTDD
後編について • 以下は後編で扱う予定です – TDDが抱える課題 – レガシーコード上でのTDD – テストコードの改善 –
TDDで確保したテストコードの活用 – TDDの諸目的
イントロダクション
テスト駆動開発(TDD) • テストファーストプログラミングの1手法 – ユニットテスト、アサートファースト • プログラミングのアプローチ • Kent Beckが具体的な方法論としてまとめる
TDDのステップ 1. 最初にテストを書いて実行 (RED) 2. テストをパスするまでコード を実装(GREEN) 3. コードをきれいにする (REFACTOR)
これを繰り返しインクリメンタルに実装を進める RED GREEN Refactor
コードの4象限とTDDのサイクル きれい 汚い うごかない うごく Green Refactor Red
ハンズオン課題1
実装仕様 • うるう年判定関数(グレゴリオ暦) – 西暦年が4で割り切れる年は閏年 – ただし、西暦年が100で割り切れる年は平年 – ただし、西暦年が400で割り切れる年は閏年
TDDの概要
TDDの定義 • 「テスト駆動開発入門」 Kent Beck – バイブル的存在 • 方法論としての厳密な定義を強制しない •
「TDDはXPのように絶対的ではない」 – テスト駆動開発入門 • 有力な原則やアドバイスで方法論を構築
TDDの原則 • Robert C.Martinの3原則 – 失敗するユニットテストを成功させるためにしか、 プロダクトコードを書いてはならない。 – 失敗させるためにしか、ユニットテストを書いては ならない。コンパイルエラーは失敗に数える。
– ユニットテストを1つだけ成功させる以上に、プロ ダクトコードを書いてはならない。
TDDのテストの原則 • 完全な自動テストであること • 自己完結できるテストであること • 初期設定、実行、検証がセットとなっている • 一部で手動作業が必要、といった形を避ける •
繰り返し可能なテストであること • 何度実行しても結果は同じ • 独立して実行できるテストであること • 一緒/個別に実行しても結果は同じ • 順不同でも結果は同じ • 十分に細粒度であること • 作業の最小単位ごとにテストを書く
TDDの手順 (Red、Green、Refactorのサイクル)
TDDの流れ RED 失敗するテストを書く GREEN テストをパスするまで コードを書く Refactor テストを使って リファクタリングする
Redのステップ • テストを書く – 失敗するテストを継ぎ足す – 小さなテストを書く • ここで書いたテストが実装作業単位になる
Greenのステップ • テストをパスするコードを書く – Redのステップで失敗しているテストを通すまで コードを書く – テストが失敗状態のまま他の作業に移らない
Refactorのステップ • Greenで追加・変更したコードをきれいにする – テストがパスした状態を維持する – 既存のテストで可能なリファクタリングを行う • 新規実装の場合はRedのステップに移る –
Refactorのために新たにテストを追加してもよい – 実行するほどでもないなら飛ばしてもよい
TDDの利益
TDDの利益 1. すばやく継続的なフィードバック 2. 作業の細分化、ステップバイステップの実現 3. 単体テスト容易性の向上
すばやく継続的なフィードバック • プログラミングの進展やミスのフィードバック を即時に・継続的に得られる – 単体テストが意図通り動いていない – リファクタリングのデグレード – プログラミングミス
• 生み出されたテストをリグレッションテストと して継続実行することで、デグレードの迅速 な検出をチームとして推進できる
作業の細分化・ ステップバイステップの実現 • プロダクトコードの追加、テストコードの追加 を細切れで進める。ステップバイステップで 進める – Defect Localization。バグの埋め込みの範囲を 小さくする
– 確実に進む。不完全なものは一つに絞り込む • テストの失敗しているところが次の作業
単体テスト容易性の向上 • TDDでは、単体テストが通るプロダクトコード しか作られない • テストファーストによって、プロダクトコードが 単体テストに対して最適化される • 効果: –
リファクタリング容易性の向上 – 単体テストの網羅性の向上 – リファクタリングやCover&Modify(後編)が容易 になり、コードの品質(移植性等)が向上する
TDDでのテクニック (Red、Green、Refactorのサイクルの 中でのテクニック)
「テスト駆動開発入門」における 基本パターン 1. Fail It 2. Fake It 3. Triangulate(三角測量)
1~3を十分に積み重ねた後 4. Obvious Implementation(明白な実装)
1. Fail It @Test Pubic void test引数を倍にして返す() { assertEquals(6, 引数を倍にして返す(3));
} テストコード Pubic int引数を倍にして返す(int input) { } 製品コード スケルトン、あるいは未実装
2. Fake It @Test Pubic void test引数を倍にして返す() { assertEquals(6, 引数を倍にして返す(3));
} テストコード Pubic int引数を倍にして返す(int input) { return 6; } 製品コード
3. Triangulate(三角測量) @Test Pubic void test引数を倍にして返す() { assertEquals(6, 引数を倍にして返す(3)); assertEquals(10,
引数を倍にして返す(5)); } テストコード Pubic int引数を倍にして返す(int input) { return 6; } 製品コード
4. Obvious Implementation (明白な実装) @Test Pubic void test引数を倍にして返す() { assertEquals(6,
引数を倍にして返す(3)); assertEquals(10, 引数を倍にして返す(5)); } テストコード Pubic int引数を倍にして返す(int input) { return input * 2; } 製品コード
ステップの調整 • ステップ、粒度は不安や慎重度に応じて調整 • 慎重な場合 – Fail It(Compile Error)→Fait It(Red)→Fake
It→ Triangulate→ Obvious Implementation • 平易な場合 – Fail It→ Obvious Implementation
TDDでの テストコードの整理
テストコードの整理 • テストコードを整理する場面 – 製品コードの変更に追従するために • コードの変更で冗長になったテストを最適化する • インターフェースの変更に追従する –
テストコードの品質を向上させるために • テスト設計を損なわないようにテスト実装を整理する • TDDの中で生まれたテストの冗長性を整理する • 保守のために保守性を上げる
テストコードの整理 • TDDの定義には含まれないが必要な作業 テストの品質問題を放置するとTDDを非効率なも のにする – 製品コードのリファクタリング容易性や拡張性を損な う(Fragile Test等) –
テストコードがミスを見逃すようになる(Buggy Test等) – TDDの作業量を無用に増大させる(Assertion Roulette,Slow Test等) – テストコードの保守性を低下させる(Obscure Test等
テストコード整理のタイミング • TDDのサイクル上のタイミング – Red→Green→Refactor→テストコード整理 • リファクタリングに合わせて最適化 – (Red→Green→Refactor)を繰り返す→テストコード 整理
• 蓄積したテストコードをまとめ上げる • その他、コミット前/CIなどへの組み込み直前/ 派生先への流用前等の適宜のタイミング – プロダクトコードのリファクタリングと同じ
テストコード整理の目標 • TDDのテストの原則遵守を目指す – 完全な自動テストであること/自己完結できるテストであること/ – 繰り返し可能なテストであること/独立して実行できるテストであること – 十分に細粒度であること •
テストの保守性を確保する – 可読性/変更性/デバッグ容易性 • 「単体テスト」としての妥当性を目指す – 簡単に実行できるべき – テストは品質向上を手助けしてくれるべき – テストはテスト対象の理解を手助けしてくれるべき – テストはリスクを削減してくれるべき – テストは簡単に実行できるべき – テストは簡単に変更・保守できるようにするべき – システムの機能拡張時でもテストの変更は最小限になるようにすべき
テストコードの整理での 注意点・アドバイス • 慎重に行う – テスト設計の等価性を保証する技法は不十分 • 一時的であってもLost Testは避ける –
アプローチは基本的にParallel Change。テストの削除 は代替が十分に揃ってから • 製品コードを工夫する – テスタビリティを観点に製品コードを組む • 言語、ツールの力を借りる – IDEのリファクタリング機能といった、低リスクな機能を 積極活用
テストコード整理の例 • Custom Assertionによる置換 – まとまったAssertionのセットを一つのAssertionに まとめる – Assertionのセットの重複記述を解消する •
注意 – 利点・欠点共にある – CUnitといった行番号情報が重要なフレームワー クではプリプロセッサでまとめたほうが良い
Custom Assertionによる置換 @Test Pubic void test3() { …. assertEquals(bar.a(), foo.a());
assertEquals(bar.b(), foo.b()); assertEquals(bar.c(), foo.c()); } @Test Pubic void test2() { …. assertEquals(bar.a(), foo.a()); assertEquals(bar.b(), foo.b()); assertEquals(bar.c(), foo.c()); } @Test Pubic void test1() { …. assertEquals(bar.a(), foo.a()); assertEquals(bar.b(), foo.b()); assertEquals(bar.c(), foo.c()); }
Custom Assertionによる置換 @Test Pubic void test3() { …. assertEquals(bar.a(), foo.a());
assertEquals(bar.b(), foo.b()); assertEquals(bar.c(), foo.c()); } @Test Pubic void test2() { …. assertEquals(bar.a(), foo.a()); assertEquals(bar.b(), foo.b()); assertEquals(bar.c(), foo.c()); } @Test Pubic void test1() { …. assertEquals(bar.a(), foo.a()); assertEquals(bar.b(), foo.b()); assertEquals(bar.c(), foo.c()); } static void assertHoge(fuga exp, fuga act) { assertEquals(exp.a(), act.a()); assertEquals(exp.b(), act.b()); assertEquals(exp.c(), act.c()); }
Custom Assertionによる置換 @Test Pubic void test3() { …. assertHoge(bar, foo);
} @Test Pubic void test2() { …. assertHoge(bar, foo); } @Test Pubic void test1() { …. assertHoge(bar, foo); } static void assertHoge(fuga exp, fuga act) { assertEquals(exp.a(), act.a()); assertEquals(exp.b(), act.b()); assertEquals(exp.c(), act.c()); }
TDDを支える テスティングフレームワーク
TDDを支える テスティングフレームワーク • 自動化された単体テストをサポートするフ レームワークが多用される • 現時点ではxUnitフレームワークが一般的 – TDDで用いられる他のフレームワークでもxUnitと 同等の機能を持つことが多い
xUnit • Kent BeckがSmalltalk用に作成したテスティン グフレームワークが元祖 • その設計思想に従った単体テスティングフ レームワークの総称 • JUnit、SUnit、NUnit、cppUnitなど
xUnit • 階層構造、カテゴリによりテストを構造化 – Test Assert : Test Method :
Test Suite • Four-Phase Testでテストの独立性や保守性を 向上 – Setup→Exercise→Verify→Teardown • 開発言語の設計能力を活用しテストの保守 性を向上 – JUnit:Test Suiteの定義にClass、カテゴリの定義に アノテーション等を活用
xUnitの構造 http://xunitpatterns.com/XUnitBasics.html: Sketch Static Test Structure参照
xUnitの構造:基本用語解説 • SUT(System Under Test) – テスト対象 • Fixture –
テスト実行前にSUTに対して行う事前設定 • Test Runer – テストを実行Suiteを抜き出してテストを実行するプロ セス • Test Double – テスト実行時に、SUTが依存するコードやコンポートン との代替となるもの
ハンズオン課題2
課題 • 本リストを管理するClass – 書名と価格を格納できる – 格納順でもっとも古い本を削除できる – 格納順の番号で本を検索できる –
書名から価格を検索できる
ソフトウェアテスト手法 としてのTDD
TDDの主な対象工程
TDDのテストはテストなのか? • テスト • ただしテストの目的・観点が一般的なものと 異なる – 網羅的なバグ出し・QAが主な目的ではない – プログラマのためのプログラマによるテスト
TDDのテストは 工程検査の単体テスト設計と 何が違うのか? • 傾向としての違い: – テストファーストで作られる – 十分性はプログラマの主観で判断される –
プログラマの都合で作られ、保守される
TDDは どのようなテスト設計技法なのか? • テスト設計技法ではない • TDDでは特定のテスト設計技法を強制しない – 一般的な単体テスト設計技法と両立可能 – ただTDDとの相性として、技法の適・不適はある
TDDと従来の単体テスト設計は 一致させられるか? • 一致は可能。しかし一般的に非効率 – 「実装作業のサポート」という観点が抜け落ちた テスト設計は、TDDの効率を削ぐ – 「実装作業のサポート」という観点で設計されたテ ストは、保証目的のテスト設計として冗長性や抜
けを持つことが多い
TDDと従来の単体テスト設計は 一致させられるか? • 一致でなく共存による相乗効果を目指す – TDDでは整合性のある単体テストを内包するよう に実装を進める – 工程保証ステップではTDDで確保されたテスト容 易性・テストコードを活用する
具体的には、TDDのテストを、自分たちのテストに 拡張・改善する
共存プロセス • Outside-InのTDD – 上位設計をブレークダウンしていく http://xunitpatterns.com/Philosophy%20Of%20Test%20Automation.html: “Outside-In” development of functionality
supported by Test Doubles. 参照のこと
共存プロセス • 詳細設計とTDDで反復フローを構成 設計・テストの方向性を提示 実装中はFixしない 設計改善をフィードバック
まとめ • TDDとは/では – Red→Green→Refactorのサイクルでコードとテスト を蓄積していく – 自動化された単体テストで、継続的かつ即時の フィードバックを実装作業に返す –
単体テスト容易性と単体テストコードを確保する