テスト駆動開発のエッセンス

27
テスト駆動開発のエッセンス 自動テストセットは欲しいけれど テストファーストが嫌いな人のための 2009-03-22 山本 博之@名古屋アジャイル勉強会

description

テスト駆動開発についてまとめた資料です。2008年4月にFLOSS桜山で発表した資料を一部修正したものです。

Transcript of テスト駆動開発のエッセンス

Page 1: テスト駆動開発のエッセンス

テスト駆動開発のエッセンス

自動テストセットは欲しいけれどテストファーストが嫌いな人のための

2009-03-22山本 博之@名古屋アジャイル勉強会

Page 2: テスト駆動開発のエッセンス

アジェンダ

● テスト駆動開発とはどんなものか● 自動テストセット● ユニットテストの書き方● テスト駆動開発のテストはテストじゃない● プログラミングと洞察● おまけ:C言語版ユニットテストセットの例

Page 3: テスト駆動開発のエッセンス

典型的なテスト駆動開発

● 単体テストフレームワーク(xUnit)の利用● 開発者自身によるユニットテストの作成● ユニットテストセットの常時再実行● (まずテストケースを書き、それにパスするよ

うにコードを書く)

Page 4: テスト駆動開発のエッセンス

テスト駆動開発はどこから来たか

● テストファースト(Test-First)– アジャイルソフトウェア開発手法eXtreme

Programming(XP, 1999)のプラクティスのひとつ– 「コードを変更する前に、失敗する自動テストを作

成する」● 後にテスト駆動開発Test-Driven

Development(TDD, 2002)という名前で開発手法としてXPから独立– 自動テストセットを用いる開発方法– XP/アジャイルに限定しない

Page 5: テスト駆動開発のエッセンス

テスト駆動開発の起源?

(著者ケント・ベックが12歳の時に読んだ)「この書籍には、実際に入力したテープから想定される出力テープを打ち込み、実際の結果が予想された結果と一致するまでコーディングすることが提唱されていた。」

「テスト駆動開発入門」

Page 6: テスト駆動開発のエッセンス

テスト駆動開発のコンセプト

● 目標:動作するきれいなコード– 開発を予測可能にする– コードからノウハウを得る機会をつくる– ソフトウェアのユーザーの生活を向上させる– 開発チームメンバー同士の信頼を築く– コードを書くのが心地よい

Page 7: テスト駆動開発のエッセンス

テスト駆動開発の構成要素

● 自動テストセット– ソフトウェアの振舞いを自動検証する– テストプログラム(群)

● 開発手順– レッド・グリーン・リファクタリングのリズム– テストを少し、コードを少し

● 自動テストセットこそ重要● 開発方法は手段

Page 8: テスト駆動開発のエッセンス

自動テストセットこそ重要

● 自動テストセットは何の役にたつか– デグレード検出– ドキュメント– デバッグツール– 進捗の指標

● 安心、勇気、スピード、積極性、等々、を与えてくれる

● とはいえ、タダではない

Page 9: テスト駆動開発のエッセンス

自動テストセットの収支

● 作成にはコストがかかる● メンテナンスにもコストがかかる● 価値を高め、コストを下げる工夫が必要

Page 10: テスト駆動開発のエッセンス

自動テストセットの価値を高める

● 早く使い始める– プログラマ自身が自分でテストを書く

● 繰り返し利用する– コンパイルするたびに実行する– 継続的にビルドして常時実行する– インクリメンタル・イテレーティブな開発

Page 11: テスト駆動開発のエッセンス

自動テストセットのコストを下げる

● 作成コストを下げる– テストを書きやすい設計

● 結合度を下げ凝集度を上げる– テストを書きながらコードを書く

● メンテナンス性を上げる– 壊れにくくする

● 依存性を減らす– 単体テストを自動化する– ひとつのテストはひとつのことしかテストしない– モックオブジェクトの使用

Page 12: テスト駆動開発のエッセンス

テスト駆動開発の前提

● 有機的(進化的)設計● プログラマがテストも書く● 迅速なビルドを可能とする開発環境● テスト可能な設計● いずれも、自動テストセットの利益を高める

か、あるいはコストを下げるために必要なこと

Page 13: テスト駆動開発のエッセンス

ユニットテストの書き方

● 厳格なテストファーストでなくてもよいと思います

● クライアントコード例を書くつもりで– クライアントコードをユニットテストとして書いてみる

– 実装する–必要に応じてテストを足す

● プログラミングインタフェースさえ明確にできていれば、後からなんとかできる

Page 14: テスト駆動開発のエッセンス

全肯定テストセット作成術

● 既存コードの振舞い(インプットとアウトプット、状態の変化)を、デバッガで調べて、ユニットテストとして記述する

● テストセットができてしまえばこちらのもの– デグレード検出できるので、リファクタリングだってできる

Page 15: テスト駆動開発のエッセンス

テスト駆動開発の手順のねらい

● テストをすこし、コードをすこし– 作りすぎを防ぐ– 構造化を促す– 安心感を得る

● レッド・グリーン・リファクタリング– リズムを与える

● 手順はオプションですが、理に適っていると思います

Page 16: テスト駆動開発のエッセンス

ユニットテストフレームワーク孝

● シンプルなもので十分– MinUnit(C言語用)はたった3行– 僕が普段使っている自作ライブラリはマクロ2個だ

けです● Makefileとサンプルといつでも相談にのる姿勢

が重要

Page 17: テスト駆動開発のエッセンス

自動テストセットが満たすべき性質テストの自動化を考えるときの参考に

● 簡単実行

– 言うまでもなく...● 自己検証

– OK/NGまでプログラムに判断させる● 繰り返し可能

– 何度実行しても同じ結果が得られること● 独立性

– (なるべく)環境や外部のコンポーネントに依存しない– テストケースの実行順に依存しない

Page 18: テスト駆動開発のエッセンス

いまどきのフレームワークに求められるもの

● アプリケーションをテストしにくいフレームワークは困る

● ユニットテストをいかにサポートするか● デプロイしなくても単体でテストしたい● xUnitなどデファクトの単体テストフレーム

ワークに載せたい● ドライバやモックの機構を提供する

Page 19: テスト駆動開発のエッセンス

テスト駆動開発のテストはテストじゃない?

● テスト駆動開発のテストは仕様記述– プログラムが仕様に適合していることを示す

● 実行すればただちに示される● デバッグ(バグ探し)ではない

Page 20: テスト駆動開発のエッセンス

機能テストについて

● 機能テストがあってのユニットテスト– ダブルチェック

● テストビューからコントローラとモデルへのテストを自動化するのが基本

● UIのテストやDBMSまで結合したテストはコストが高いので、すべてをやろうとせず(単体テストや機能テストで補間して)、観点を絞って行うとよいのでは

Page 21: テスト駆動開発のエッセンス

テスト駆動開発が合う開発合わない開発

● 探索的な開発とは相性が良くない?– 割り切って、後からテストを書く– あるいは、後からテスト駆動で書き直す

● 制約を課すことで洞察を得るのが、手法としての目的– アジャイル/リーン(トヨタ式)に共通する特徴

Page 22: テスト駆動開発のエッセンス

プログラミングと洞察

当時の一般的手法は、とりあえずプログラムを書いてから問題ないことを検証するというものであった。ダイクストラはこれに対して、検証に時間がかかって面倒であるし、プログラムの開発手法に何ら洞察を与えない点が問題であるとした。

エドガー・ダイクストラ - Wikipedia

テスト駆動開発は、プログラミング中の決定とフィードバックとのギャップの認識、およびそのギャップを制御する技術である。

「テスト駆動開発入門」ケント・ベック

Page 23: テスト駆動開発のエッセンス

参考文献

● XPエクストリーム・プログラミング入門 第二版

● テスト駆動開発入門– ケント・ベック著+長瀬嘉秀訳...かなり厳しいです

が、原典なので。よく読むと、すべて書いてあります。

● xUnit Test Patterns - Gerard Meszaros– テスト駆動開発の考え方とパターンを網羅した決定

版。900ページ位ある。翻訳中との噂。

Page 24: テスト駆動開発のエッセンス

「テスト駆動開発は、コードに思い入れのある変わり者にも適している」

「テスト駆動開発入門」ケント・ベック

Page 25: テスト駆動開発のエッセンス

付録:C言語ユニットテストセット例1/3

テスト呼出しヘルパマクロ

/* unit_test.h */#ifndef UNIT_TEST_H#define UNIT_TEST_H

#include <stddef.h>#include <stdio.h>

extern size_t unit_test_count;

#define RUN_TEST(test) do { printf(#test "\t"); fflush(stdout); \test(); printf("OK\n"); ++unit_test_count; } while(0)

#define RUN_SUITE(suite) do { void suite(void); suite(); } while(0)

#endif

Page 26: テスト駆動開発のエッセンス

付録:C言語ユニットテストセット例2/3

main関数。テストスイートランナー。

/* main.c */#include "unit_test.h"

size_t unit_test_count = 0;

intmain(){ RUN_SUITE(foo_test); /* and more */

printf("OK (%d tests)\n", unit_test_count); return 0;}

Page 27: テスト駆動開発のエッセンス

付録:C言語ユニットテストセット例3/3

テストソースファイル。スイートとテストケース。

/* foo_test.c */#include <assert.h>#include "unit_test.h"

voidtest_foo_hoge(void) // テストケース{ assert(1 + 1 == 2); //パスすること前提なのでassertで十分}

voidfoo_test(void) // テストスイート{ RUN_TEST(test_foo_hoge); /* and more */}