ESEC2011 ブース内セッション 単体テスト設計のコツ 日本システム開発株式会社 http://www.nskint.co.jp Copyright 2011 日本システム開発株式会社 All Rights Reserved
目次 1. ユニットテストについて知っておかないといけないこと 1-1. 品質問題の原因とユニットテストの関係 1-2. ソースコードレビューとユニットテストの違い 2. 有効なユニットテストデータの与え方 [ 事例 ] 2-1. [ 事例 1] 有効な値が 10~20 の unsigned short 変数 2-2. [ 事例 2] 参照のみ行う領域の確認 2-3. [ 事例 3] メモリ破壊が起きるコピー処理 3. 危険コードに対するテストデータの与え方 [ 事例 ] 3-1. [ 事例 1] 欠陥となる可能性がある符号変換処理 3-2. [ 事例 2] オーバーフローに誘発されたゼロ割処理 4. ユニットテストまとめ 2
1. ユニットテストについて 知っておかないといけないこと 3
1-1 品質問題の原因とユニットテストの関係 不具合の原因の割合 約 6 割がソフトウェアの不具合 引用 : 2010 年版組込みソフトウェア産業実態調査報告書 - 事業責任者向け調査 - ( 経済産業省 ) 品質の悪い製品を作り出す原因の一部は ユニットテストにある ユニットテストは製品不具合の原因に直接的直接的または関与する または間接的間接的に 4
1-1 品質問題の原因とユニットテストの関係 製品欠陥に対して直接的に関与する場合 ユニットテスト欠陥の流出 ユニットテスト工程で検出すべき欠陥を 検出できなかった そのまま製品が出荷されたため欠陥が発生し 品質問題による損失を発生させた レビュー ユニットテスト 統合テスト 検出できなかった ユニットテスト工程以降でも欠陥を検出できず そのまま流出させてしまった 5
1-1 品質問題の原因とユニットテストの関係 製品欠陥に対して間接的に関与する場合 ユニットテスト以降のテストが不十分 ユニットテスト工程に不備があり その後のテスト工程で行うはずであった機能テストなどが十分に実施できなかった 必要なテストが不十分なまま出荷されたため欠陥が発生し 品質問題による損失を発生させた 予定 ユニットテスト統合テスト 実績 検出できなかった ここで検出 ユニットテスト欠陥の対応 統合テストが十分にできず 統合テストで検出すべき欠陥を流出させてしまった 直接の原因が統合テストであるため そもそもの原因であるユニットテストに改善の意識が向きにくい 6
1-2 ソースコードレビューとユニットテストの違い ユニットテストに対するよくある誤解 ソースコードレビューをしっかり行えば ユニットテストは実施しなくても問題ないのでは? ユニットテストは実施しなければならない ユニットテストとソースコードレビューの工程の目的は大きく異なるため ユニットテスト工程を省略することは不可能 ユニットテストを実施しないと 他の工程は本来の目的を果たすことができない 仮に現状ユニットテスト工程を省略していて問題が出ないとしても 今の開発体制が崩れたら問題が発生する可能性は大 7
1-2 ソースコードレビューとユニットテストの違い ソースコードレビューとユニットテストの比較 ユニットテストのコツとして ソースコードレビューとの違いを認識しておくことが重要 同じ 工程評価対象評価範囲インプット評価内容欠陥検出範囲品質保証 ソースコードレビュー ソースコード 関数詳細設計静的分析 仕様網羅性確認コーディング規約適用確認冗長性確認コメント確認タイミング欠陥確認 ( ロバスト性確認 ) 不可能 ユニットテスト 実行モジュール 関数詳細設計動的分析 仕様網羅性確認コードカバレッジ確認ロバスト性確認 可能 ( 動作させた範囲 ) ソースコードレビューとユニットテストは 評価範囲 と インプット は同じだが それ以外は異なる 8
1-2 ソースコードレビューとユニットテストの違い ソースコードレビュー以外では検出できない欠陥 Q. タイミングによる欠陥を検出できるか? : : void void func() func() {{ if if (1 (1 == == a) a) {{ b b = = a; a; }} return; return; }} : : a が 1 の時に b を a(1) にすることを期待しているが 割り込みが上がると期待しない結果になる ここで割り込み a = 3; ユニットテスト以降の工程でこの状況を確実に作り出すことはできない A. ソースコードレビューなら タイミングによる欠陥を検出できる 9
1-2 ソースコードレビューとユニットテストの違い ユニットテスト以外では検出できない欠陥 Q. コンパイル環境によって動作が異なる場合 必ず正しいソースコードレビューが行えるか? : : unsigned unsigned char char a a = = 255, 255, b b = = 255; 255; unsigned unsigned short short c; c; c c = = (unsigned (unsigned short)(a short)(a + + b); b); : : ある環境では 510 ある環境では 254 A. レビューアが全てのコンパイラの動作を把握することはほぼ不可能であるため 必ず正しく行えるとは限らない 実際に動作させるユニットテストで差分を検出する必要がある 10
1-2 ソースコードレビューとユニットテストの違い まとめ 1. 欠陥検出範囲はソースコードレビューの方が広いソースコードレビューの方が多くの欠陥を検出できる 2. ユニットテストは機械的に全ステップ評価する唯一の工程目視で行うソースコードレビューに対し ユニットテストは機械的にテストすることでヒューマンエラーを防ぐ ユニットテストの後工程では全ステップに対してテストを行わないため ユニットテストでステップに対する評価を完了しておく必要がある 3. ソースコードレビューは品質証明にならないソースコードレビューはレビューアがNGと判断した箇所だけに指摘が出るため 他に欠陥が10 件潜伏している可能性も 100 件潜伏している可能性もある ユニットテストは品質証明になる ユニットテストは与える INPUT に対するテスト結果を記録として残すため この INPUT を与えれば正常に動作する ということを証明できる (INPUT を網羅する必要あり ) ソースコードレビューとユニットテストは共に必要 ( それぞれで何を重視すればよいか? が重要となる ) 11
2. 有効なユニットテストデータの 与え方 [ 事例 ] 12
2 有効なユニットテストデータの与え方 [ 事例 ] 有効なユニットテストにするために重要なこと どのようなテストケースを作成するか どのようなテストデータを与えるか このようなユニットテストを OJT で行うことは難しい ( 現場に指導できる人材が少ない ) 有効なユニットテストを考えるための事例 [ 事例 1] 有効な値が 10~20 の unsigned short 変数 [ 事例 2] 参照のみ行う領域の確認 [ 事例 3] メモリ破壊が起きるコピー処理 13
2-1 [ 事例 1] 有効な値が 10~20 の unsigned short 変数 同値分割とは 同値分割 同値分割はテストデータを抽出する手法の一つ 起こりうるすべての事象を洗い出し 同じ事象を発生させる入力値の集合を作成 この入力値の集合を同値クラスと呼ぶ 同値分割では 同値クラスから代表値を選出し テストデータとする 14
2-1 [ 事例 1] 有効な値が 10~20 の unsigned short 変数 例 ) 有効な値が 10~20 の unsigned short 変数の場合 long func(unsigned short data) { if ((10 <= data) && (20 >= data)) { : } : } 10 20 を境界とした同値クラスに分割 分割した同値クラスから代表値を選出 同値クラスの代表値 同値クラス 同値クラス 同値クラス 0 9 10 15 20 21 50 Q.data の初期値に何の値を指定するか? A. 例えば 0 15 50 を指定したとする しかし 50 では欠陥が検出できない可能性がある 15
2-1 [ 事例 1] 有効な値が 10~20 の unsigned short 変数 例 ) 有効な値が 10~20 の unsigned short 変数の場合 検出できない欠陥と対策 50をテストデータとして指定 50 上位バイト 0000 0000 下位バイト 0011 0010 2 バイトの変数である 有効範囲は 10~20 の 1 バイト範囲である 下位バイトが有効値と同じビットの値の評価が正しくできない可能性がある ( 例えば 266 が有効値とみなされる欠陥 ) 266をテストデータとして指定 266 上位バイト 0000 0001 下位バイト 0000 1010 対策変数を2バイトで評価しているか検証できる値を使用する ( 例えば266) 上位バイトを評価に使用しているか確認できる 下位バイトが有効範囲内であれば 誤った判断をしていないか確認できる 16
2-2 [ 事例 2] 参照のみ行う領域の確認 例 ) 参照のみ行う領域を更新していないことを確認する場合 関数 func() は外部変数 flag を参照している ( 更新はしない ) 外部変数 flag 参照 flag が更新されていないことを確認 func() を呼び出す前と後で flag の値が同じであればよい flag に xx を設定 関数 func() func() 呼び出し flag が xx か? flag が xx なら 更新していない Q.flag の初期値に何の値を指定するか? A. 例えば 1 を指定したとする しかし 1 では欠陥が検出できない可能性がある 17
2-2 [ 事例 2] 参照のみ行う領域の確認 例 ) 参照のみ行う領域を更新していないことを確認する場合 検出できない欠陥と対策 関数 func() は外部変数 flagを参照している ( 更新はしない ) flagの初期値として 1 を指定 func() 呼び出し前 flag = 1 func() 呼び出し後 flag = 1? 外部変数 flag 参照 関数 func() 偶然初期値と同じ値に flag を更新している可能性がある 一般的によく使用する値 (1 や 0) では 意図しない所で偶然初期値と同じ値に書き換えられている可能性がある 対策マジックナンバー (0xaaなど) を指定する値を変えて複数回テストする 18
2-3 [ 事例 3] メモリ破壊が起きるコピー処理 例 ) 配列をコピーする場合 From To Size を指定してコピーを行う処理 (From To はメモリのどこでも指定できる ) From To 元データ コピー先 Size 元データ (From) がコピー先 (To) へ Size の値だけコピーできていればよい 多くの場合 サイズに着目したテストを実施している (0 配列の最大値を指定 ) しかし サイズだけに着目したテストでは 欠陥が検出できない可能性がある 19
2-3 [ 事例 3] メモリ破壊が起きるコピー処理 例 ) 配列をコピーする場合 検出できない欠陥 コピー元データの開始位置 (From) コピー先データの開始 (To) データサイズ (Size) の関係によって発生するメモリ破壊 メモリ破壊が起きるパターン 1 元データの後方とコピー先の領域が重なっている場合 元データ コピー先 先頭から順にコピーすると 後方のデータを使用する時にはコピー処理により上書きされてしまっている 2 元データの前方とコピー先の領域が重なっている場合 コピー先 元データ 末尾から順にコピーすると 前方のデータを使用する時にはコピー処理により上書きされてしまっている 20
2-3 [ 事例 3] メモリ破壊が起きるコピー処理 例 ) メモリ破壊が起こるコピー処理 対策 コピー処理がある場合の正しいテスト観点 From To Size この 3 つの組み合わせが必要 From 元データ To コピー先 Size 組み合わせを考えないとテストに抜けが発生する 21
3. 危険コードに対する テストデータの与え方 [ 事例 ] 22
3 危険コードに対するテストデータの与え方 [ 事例 ] 危険コードに対するテストデータ 危険コードは多くの値で正常に動作するが ある特異な値を与えた場合に欠陥となる危険コードを考慮しないとテストが漏れる 本章で紹介する事例 [ 事例 1] 欠陥となる可能性がある符号変換処理 [ 事例 2] オーバーフローに誘発されたゼロ割処理 23
3-1 [ 事例 1] 欠陥となる可能性がある符号変換処理 例 )signed 変数を unsigned 変数として扱いたい場合 long func(signed char arg) { : if (0 > arg) { arg = -arg; // 符号変換処理 } : } 符号変換処理としてよく実装されるが 欠陥が潜んでいる これまで複数のプロジェクトで同じ誤りがあった Q.arg に何の値を指定するか? A. 例えば 10-10 を指定したとする しかし 10-10 では欠陥が検出できない可能性がある 24
3-1 [ 事例 1] 欠陥となる可能性がある符号変換処理 例 )signed 変数を unsigned 変数として扱いたい場合 検出できない欠陥とポイント long func(signed char arg) { : if (0 > arg) { arg = -arg; // 符号変換処理 } : } この符号変換処理において 符号を変換できない値が 1 点だけ存在する arg に -128 を与えた場合のみ 符号変換処理を行っても符号が変換されないため unsigned 変数として扱うことができない ポイント argに-128が指定されないことを確認する -128が指定される場合 指定されても問題ないことを確認する 25
3-2 [ 事例 2] オーバーフローに誘発されたゼロ割処理 例 ) 演算後に除算している場合 long func(unsigned char arg1) { :: a = (b / (unsigned char)(arg1 + 5)); :: } arg1 を +5 しているため ゼロ割が発生しないと判断を誤りやすい arg1 = 0 long func() {{ :: }} Q.arg1 に何の値を指定するか? A. 例えば 0 10 255 を指定したとする しかし 代表値 最小値 最大値だけでは欠陥が検出できない可能性がある 26
3-2 [ 事例 2] オーバーフローに誘発されたゼロ割処理 例 ) 演算後に除算している場合 検出できない欠陥とポイント long func(unsigned char arg1) { :: a = (b / (unsigned char)(arg1 + 5)); :: } この演算処理において ゼロ割を発生させる値が 1 点だけ存在する arg2 に 251 を与えた場合のみ +5 するとオーバーフローするため 除算の分母が 0 になり ゼロ割が発生する ポイント特定の値を与えられた場合のみ欠陥となるコードがあることを認識してテストを行う コードを見ないと有効なテストができないケースもあり得ることを認識する 27
4. ユニットテストまとめ 28
4 ユニットテストまとめ 品質の良いユニットテストのために必要なもの ユニットテストの位置づけについての正しい理解 ( 特にソースコードレビューとの違い ) ユニットテストは開発において非常に重要な工程だが 正しい知識を持って実施できていることが少ない テストケース設計の知識いかに有用なテストケースを作るかが重要 テストデータの与え方の知識テストケースが正しくても 与えるテストデータによって欠陥が検出できるかできないかが決まる 3つの知識が揃って始めて 品質の良いユニットテストができる ただし 3つの知識をOJTだけで習得するのは難しい Off-JT で3つの知識を習得する必要がある 29
単体テスト関連サービスについて 単体テスト教育 単体テストはどの開発プロジェクトでも実施しますが 明確に実施方法が定まっていません そんな開発現場で活用してもらうために単体テストに特化した教育コンテンツを揃えています C 言語版 C++ 言語版 のコンテンツがあります 本コンテンツを使用したセミナーを定期的に開催しています 主催 ガイオ テクノロジー株式会社 URL : http://www.gaio.co.jp/event/event.html 単体テスト代行サービス ソフトウェア品質を確保するために重要な工程である 単体テスト を代行します ブラックボックス ホワイトボックス ロバスト性の観点から 高品質な単体テストを実施します 単体テスト代行サービスの効果に不安がある場合 試験的に数関数に対して単体テスト代行サービスを実施することも可能です ガイオ テクノロジー株式会社の単体テスト代行サービスを支援しております テスト導入支援 テスト導入支援は単体テスト以外の内容もカバーしたサービスです 単体テスト以外 ( 設計や結合テスト ) の工程も改善しなければソフトウェア品質が向上しない場合 お客様の現場に合った方法をご提案します 例 テストガイドライン策定 テスト設計レビュー 設計書確認 ガイオ テクノロジー株式会社のテスト導入支援を支援しております ご質問 ご相談 資料請求はこちらまで : emb-sales@nskint.co.jp 30
単体テスト以外 (Android) のサービスについて Android アプリケーション開発初級編 Android OS の基礎知識を学ぶとともに Android アプリケーション開発の基本となるプログラミングモデルを講義と実習から習得します Android アプリケーション開発応用編 Android の中枢であるアプリケーションフレームワークの仕組みを理解するとともに 実際の Android アプリケーション開発でよく発生する問題点の事例 解決策などを理解することで より高度な Android アプリケーション開発のスキルを習得します Android 開発ポーティング編 ターゲットボード上の Linux システム上に Android 特有のドライバの構築と ミドルウェア Android アプリケーション等の搭載を行います また Android のチューニング技術なども習得する事により 実践的な Android 技術者の育成が可能になります ( 企画 : 株式会社エンベデッド システム開発 : 日本システム開発株式会社 ) その他必要スキル教育 (Java, デザインパターン等 ) 株式会社豆蔵との連携により Android 開発に必要なその他技術要素の習得も可能です (Java, デザインパターン,, UML 等 ) ご質問 ご相談 資料請求はこちらまで : emb-sales@nskint.co.jp 31
御清聴ありがとうございました 32