code complete ch22_developper_test
Post on 29-Nov-2014
1.657 Views
Preview:
DESCRIPTION
TRANSCRIPT
Code Complete(Ch22 Developer Testing)
2010/12/17shiumachi
http://d.hatena.ne.jp/shiumachi/http://twitter.com/shiumachi
http://www.facebook.com/sho.shimauchi
Agenda
● 22章 開発者テスト● 22.1 ソフトウェア品質における開発者テストの役割● 22.2 開発者テストの推奨アプローチ● 22.3 テストに関するあれこれ● 22.4 典型的エラー● 22.5 テスト支援ツール● 22.6 テストを改善する● 22.7 テスト記録を保存する
はじめに● この資料は、某所で開催されている「Code
Complete 読書会」での発表資料です● 原著、つまり英語版の Code Complete をベースにしているので日本語版と訳語が違う場合があります● 誤読とかも普通にありますので気づいたら指摘していただけるとうれしいです
Chapter 22 Developer Testing22 章開発者テスト
Ch. 22 開発者テスト
Ch. 20 ソフトウェア品質の展望
Ch. 21 協調コンストラクションの実践
Ch. 23 デバッグ
Ch. 29 インテグレーション
Ch. 3 コンストラクション実行のための前提条件
関連トピック
テストの種類(1)
● ユニットテスト● 一人のプログラマが書く程度のクラス、ルーチン、小さなプログラムのテスト
● コンポーネントテスト● 複数のプログラマが書く程度のクラス、パッケージ、小さなプログラムのテスト
● 統合テスト● 2つ以上のクラス、パッケージ、コンポーネント、サブシステムなどを組み合わせたテスト
● 通常はテスト対象のクラスが2つできたらすぐに開始する
テストの種類(2)
● リグレッションテスト● 以前実行して通ったテストをもう一度実行する
● システムテスト● 最終調整のためのテスト● 他のソフト・ハードとの統合テスト、セキュリティ、性能、リソース枯渇、タイミング問題などの低レベルの統合ではテストできなかった問題をテストする
● その他のテスト(この章の範囲外)● βテスト、顧客受け入れテスト、性能テスト、設定テスト、プラットフォームテスト、ストレステスト、ユーザビリティテストなど
テスト
開発者が実行するテスト 開発者以外が実行するテスト
ユニットテスト
コンポーネントテスト
結合テスト
リグレッションテスト
システムテスト
βテスト
顧客受け入れテスト
性能テスト
設定テスト
プラットフォームテスト
ストレステスト
ユーザビリティテスト
この章のスコープはこちら
ブラックボックステストとホワイトボックステスト
● ブラックボックステスト● テスターは内部構造を見れない● この章の範囲外
● ホワイトボックステスト● テスターは内部構造を理解している● この章の範囲はこちら
テストとデバッグ● テスト
● エラーを検知するためのもの● この章の範囲はこちら
● デバッグ● 既に検知されているエラーを分析・修正するためのもの
● 23章で扱う
22.1 ソフトウェア品質における開発者テストの役割
テストだけじゃ効果が薄い● ソフトウェア品質においてテストは重要である● 残念ながら多くの場合、テストだけがソフトウェア品質プログラムに使われる
● テストよりも協調開発の方が半分以下のコストでより多くのエラーを検出可能
一つのテストだけじゃ効果が薄い● 単一のテストだけでは50%以下のエラーしか検知できない● どのテストでも同じ
● 組み合わせて使用してようやく60%以下になる
テストを開発者が嫌がる理由● テストだけが、開発を終わらせることを目的としていない
● テストはエラーがないことを保証してくれない● テストそれ自体はソフトウェア品質を向上しない
● テストは開発者に「自分のコードからエラーを見つけようとすること」を要求する● でも多くの開発者はエラーなんて見つけたくない
プロジェクトに占める開発者テストの割合
2,000行程度だと25%、500,000行だと8%ほど
開発者テストの結果の使い道● 開発中における製品の信頼性のアセスメント● ソフトウェア修正のためのガイド● エラーの統計情報を活用
● 適切な研修の実施● 次回のテクニカルレビューにおける指針● 次回のテストケースの作成
22.2 開発者テストの推奨アプローチ
システマティックにテストしよう(1)
● 要件1つ1つに対し、その要件が実装されていることを確認できるテストをすること● セキュリティ、ストレージ、インストールプロセス、システム可用性などに注意– 要件定義時には見過ごされている
● 設計1つ1つに対し、その設計が実装されていることを確認できるテストをすること● テストケースはできる限り早く、遅くとも設計フェーズで計画すること
システマティックにテストしよう(2)
● 「基礎テスト」を詳細テストケースに加える● データフローテスト● コードカバレッジ100%にするためのテスト
● 開発組織内で作り上げられてきたエラーチェックリストを活用する
● →テストケースを設計する 要件バグ・仕様バグ→が減る エラー修正コストが減る
テストファーストしよう
理由:安上がりだからバグを仕込んでから修正までの時間が短いほどコストは下がる
テストファーストする理由● テストラストより苦労して書くことはない● 不具合を仕込んでも発見が早くなる● コードを書く前に要件や設計について考えるようになる
● 要件バグを早く見つけやすくなる● 下手な要件はテストを書きにくい
● テストケースを書いておけばテストラストも以前と同様にできる
テストファーストはとっても有用でも銀の弾丸じゃないので過信は禁物!
開発者テストの限界● クリーンテスト(正常系テスト)になりやすい
● 未成熟な組織は正常系:異常系のテスト比率が5:1● 成熟した組織だと1:5 (25倍!)
– 正常系を減らしているのではなく、異常系の数が25倍● カバレッジに対して楽観的になりやすい
● ちゃんとカバレッジチェックツール使おう
22.3 テストに関するあれこれ
そんなにテストできねえよ!● 名前・住所・電話番号の全パターンのテスト
● 26^20 * 26^20 * 10^10 。約 10^66● 英数字だけでこれなので、日本語だとさらに多い● ノアが方舟から下りてきてから秒間1兆回テストしても、まだ1%も終わってないぐらいの量
● 当然こんなの無理なので、効率よくテストケースを選ぶ必要がある
構造化基礎テスト● 全てのコードを少なくとも1回通るような最小の数のテスト● コードカバレッジテストは最小でなくてもいい
● 作り方は簡単1.コードを真っ直ぐ通るパスとして1を足す2. ifなどの条件文、forやwhileなどのループ文、and や or それぞれに対し1を足す
3. case文ごとに1を足す。defaultがない場合はさらに1を足す
4.出てきた数字が、最小のテストケース
構造化基礎テストの実例
● 必要なテストケースは少なくとも2つ● x < 10 のケース● x >= 10 のケース
Statement1;Statement2;if ( x < 10 ) { Statement3;}Statement4;
1つ目:条件式を通らずに下まで実行
2つ目:if 文の中を通過するように実行
もっと複雑な例
もっと複雑な例
&& があるので2つカウント
データフローテスト● データの使い方もバグが入りやすいポイントなので注意● データの状態は以下の5つ
● Defined– 初期化されたが未使用の状態
● Used– 使用された状態
● Killed– free された状態
● Entered– あるルーチンに入った直後の状態
● Exited– あるルーチンから出るときの状態( return で値を返すとか)
データの状態の組み合わせ● 普通のデータフローは Defined-Used-Killed● 以下のようなケースは怪しい
Defined
Used
Killed
Entered
Exited
Defined
Used
Killed
Entered
Exited
データフローテストの作り方
全ての Defined-Used の組み合わせをテストする
if ( Condition1 ) { x = a;} else { x = b;}if ( Condition2 ) { y = x + 1;} else { y = x – 1;}
基礎テストで (false, false) のケースと(true, true) のケースを作成
(true, false) の条件でこのデータフローをテスト
(false, true) の条件でこのデータフローをテスト
もっと複雑な例でデータフローテスト
Defined-Used 1つ目
Defined-Used 2つ目
Defined-Used 3つ目
同値分割● あるデータにおいて、そのデータのとりうる全ての範囲を、同じエラーを生成するだろう部分集合に分割する
● 基礎テストとデータフローをきちんとやっていれば、相対的には重要ではない
● しかし以下のようなケースでは特に有効● ソースを読む時間がなく、仕様からテストするとき● データが複雑で、しかもその複雑さがプログラムロジックに反映しきれてないとき
同値分割の例
● m_employee[id]governmentRetirementWithheld● < MAX_GOVT_RETIREMENT
● >= MAX_GOVT_RETIREMENT
● 普通は基礎テストの時点で完了してるのでテストケースは増えない
同値分割のターゲット
エラーの推測
● ニュータイプベテランは直感でバグがわかる● コードインスペクションの過程で作成した「頻出エラーリスト」を元にテストケースを作ろう● ベテランの直感をテストに生かす方法になる
見える、私にもバグが見えるぞ!
境界値分析● 世界共通の頻出エラーといえば off-by-one
● > と >= を間違えたりとか● 境界値、境界値+1、境界値-1を全部テストする● とりうる値の最大・最小値も境界値とみなす● 値の組み合わせも考慮する
● a * b の式があるとき、abともに巨大な正数にする● あるいは巨大な負数にしたり、ともに 0 にする● 入力文字列を全て巨大にする
バッドデータのクラス● 以下のバッドデータのうち、今までのテストケースでカバーしていないものはテストする● 非常に小さいデータ(あるいはデータなし)● 非常に大きいデータ● 無効なデータ(型が違うとか)● 間違ったサイズのデータ● 初期化されていないデータ
グッドデータのクラス● 正常系データも(漏れてたら)テストする
● 一番中間の値● 正常値のうち最小・最大値
– 異常系はテストしないので境界値分析とは違う● 古いデータ
– 特に旧システムからのリプレースなどで必要
22.4 典型的エラー
エラーは偏在している
エラーの少ないクラス 394 (93%)
エラーの多いクラス 31 (7%)
エラーの少ないクラスエラーの多いクラス
IBMの調査結果より
ここを重点的に修正した→ エラー 1/10 に→ 保守コスト 45% 減→ 顧客満足度もアップ
エラーにおける20:80の法則
クラス数の比率 エラーの比率0
20
40
60
80
100
120
エラー多いクラスエラー少ないクラス
● 20%のクラスが80%のエラーを抱えている
● 極端な例だと、たった1000行に50個エラーが集中し、修正には全システムの開発コストの10倍がか
かったとか (1960 年代のIBM の OS/360 開発)
エラーの傾向
25.18
22.44
16.19
9.88
8.98
8.12
2.761.74
4.71
構造データ実装した機能コンストラクション統合機能要件テスト定義 or実行システム ,アーキテクチャ不明
※ なんか研究によって結果が随分違うらしいので参考程度に。
まだまだあるよ、エラーの統計
エラーの割合0
20
40
60
80
100
120
複数クラスに修正単体のみ
85%のエラーは原因のクラスのみの修正で済む
● エラーの三大原因● APに関する知識不足● 要件の変更や衝突● 人や組織の協調に関する問題● つまりコンストラクションでない
● コンストラクションエラーの原因● 95% プログラマ● 2% OS かコンパイラ● 2% 他のソフトウェア● 1% ハードウェア
どれも研究の一例であることに注意
まだまだまだあるよ● typo は非常に多く、その修正コストは巨額
● コンストラクションエラーの36%になることも● 損害額はひどいのになると $1.6B (1300億円)
● 設計の誤解もかなり大きい● エラーの16〜19%は設計の間違った実装
● 組織によってこの傾向はかなり変わるので、自分の組織については実際に測定するのが一番いい
コンストラクションが主たる原因● 1000行程度だと75%、500k行でも35%ぐらい● この辺もPJによってまちまち
どれだけエラーを発見できるか?● 平均的なPJだと1-25個/1000行● (少なくとも昔の)MSではリリース時に0.5個/1000行● コードリーディングと独立テストにより達成
● 「クリーンルーム開発」を使うと0.5個/1000行● スペースシャトルのソフトは0個/500,000行
● ピアレビューや統計的テストなどを活用● TSP という手法では 0.06個/1000行
テスト自身がエラーの原因になる● テストはソースコードと同じくらい丁寧に● ウォークスルーやインスペクションも活用● テスト計画を作ろう● テストケースを保存しよう● ユニットテストフレームワークを使おう
22.5 テスト支援ツール
Scaffolding
● 「足場」という意味● 「スタブ」「ドライバ」などを作る● xUnit 系はあまりにも有名● モックフレームワークでは Mockito が有名
● PHPならPHPUnit● python にもあるよ
Diff
● 説明不要● オプションの中には便利なものも色々あるので
一度は man を見よう● --side-by-side で2つのテキストを横に並べて比較できる● –幅調整は width=N。デフォルト130なので要調整● みんな知ってるよね?
テストデータジェネレータ● 力技だがやっぱり便利● プログラミングコンテストの練習時に、どうしても間違いが分からない問題はジェネレータ作って正解者のコードと比較したりしてた
カバレッジモニタ● モニタリングしないで気分だけでやってるとカバレッジは60%ぐらいらしい
● PHPUnitには標準でついてる● html出力してグラフィカルに表示できる
● python には coverage.py がある
データレコーダとかロギングとか● 間違ったテストは記録しましょう● 低レベルのテストでのツールはよく知らない● システムテストとかだと TestLink あたりが該当するか?
デバッガ● みんな大好き gdb● 1ステップづつ見ていくのはウォークスルーにもなる
● PHP だったら xdebug● firefox + vimとの連携で1ステップづつ見れる
システム乱雑化● メモリを0にしたりぐちゃぐちゃにしたり● webの世界でここまでやるとこあるんだろうか● OS とか組み込みの世界では gdb から値書き換えたりしてテストしたりするが、ここまでやるのか不明
エラーデータベース● TestLink だと統計機能がある● redmine や trac もこれに該当するかもしれない
22.6 テストを改善する
テスト改善の手法● テスト計画を作ろう● 再テスト(リグレッションテスト)
● 高品質なプロダクトには不可欠● テストを自動化しよう
● リグレッションテストは自動化必須
22.7 テスト記録を保存する
保存すべき情報● 日付、報告者、ビルドナンバー、修正日時● 問題の詳細● 関連するエラー● エラーの重要度● エラーの種類● 発見/修正にかかった時間
自分のテスト記録も保存しよう● 自分のクセがわかる● そこからチェックリストも作れる
まとめ
まとめ● 開発者テスト超重要● でも高品質なソフトにはレビューなどの手法を組み合わせることが必須
● テストファーストの方が開発時間短縮できる● テストケースの多くは形式的手法で簡単に作れる● エラーは偏在するのでそこだけ再設計しよう● リグレッションテストはテスト自動化で実行しよう
おまけ:最近の組み込み開発事情● 某社の組み込み開発事情(C言語)
● ユニットテストで網羅テストやっても費用対効果が上がらないのでカバレッジだけ確保
● 商用の静的解析ツール「coverity」を使う● 誤検知が少ないらしい
● 「組み込みの世界においては、起こる可能性のあるものは全て起きる。だからそういうものは全て潰す」
● 出てくるエラーは全て潰す● エラーを取捨選択するコストも馬鹿にならないため
参考文献
1. “Code Complete, Second Edition”, Steve McConnell, Microsoft Press, 2004
Thank you !
top related