[雑誌]ソフトウェア・テストPRESS 総集編

Vol10と総集編と混同しておりましたm(_ _)m
==


ソフトウェアテストの専門誌の総集編。過去の記事(1号~10号)を含めてCD-ROMに収録。巻頭企画は、大西さん、細川さん、町田さん、湯本さんによる特別座談会。座談会って面白いですね。Software Testing ManiaX でも確か座談会のところがありました。

特集2では、テストエンジニアのためのテスト駆動型開発入門と題して「検証指向TDD」などの記事が掲載されています。スピード感(駆動力)を損なう、単体テストはTDDとは別で実施している、前からこの考え方でTDDを実践している、といった指摘もされますが、プログラミングスキルとテスティングスキルを一緒に伸ばすという意味では十分面白い試みだと僕は思っています。例えば小さなロジックを組んで、そこでの最低必要なテスト条件数が見えていれば、必ずソフトウェア品質も上がるし、他者のコードの気持ち悪いところが見えやすくもなるでしょう。

TDD研究会
http://sites.google.com/site/tddstudygroup/

| | コメント (0) | トラックバック (0)

[本]Webアプリケーションテスト手法

Webアプリケーションテスト手法
by 水野 貴明, 石井 勇一, 新藤 愛大, 岸田 健一郎, 荻野 淳也, 安井 力, 田中 慎司
毎日コミュニケーションズ

単行本(ソフトカバー)
定価:¥ 2,940
価格:¥ 2,940
売り上げランキング: 4765位


Webアプリケーションを開発する側にたち、テスト手法を「テストの自動化(駆動開発)」「非機能テスト(性能、セキュリティ)」を中心について解説。対応言語(Perl、ActionScript、PHP、Ruby、JavaScript)の専門家が執筆しているところも特徴的。この本で学んだものをより効率的・効果的にテストへ活かすには、テスト戦略・設計にかかわる本もあわせて読むと相乗的に品質を上げることが可能かも。

| | コメント (0) | トラックバック (0)

C言語における単体テスト テスタビリティを重視した関数設計

今日は社外WGに参加。春の嵐だ!

==

僕はC言語ばっかの開発をやってるので、ここでもC言語をベースにしていろんなことを書きます。あしからず。

リリース後の欠陥発覚は手戻り工数が最もかかるものだが、その多くは単体テストで発見可能なものが多い。某静的解析ツールのベンダー資料によるとその割合は40%近くになるらしい。定量的な資料は持っていないが、僕も単体テストの質がシステム全体やプロジェクト全体の質を大きく左右すると考えている。

そこでだ。僕の考える、上質な単体テスト for C言語はこんな感じ。

  • テストしやすいという、テスタビリティ
  • (例1)
    int
    func1(
        int a1,
        char* a2,
        int a2size )

    (例2)
    foor_obj_t*
    func2(
        var_obj_t* b1 )

    まず、基本的な関数の型はint型がいろいろ都合がいい気がする。エラー番号を復帰したり、errnoを復帰したり、直感的なものがよいと思う。例1のように、文字列格納先の先頭ポインタを引数渡しする場合は、呼び元で確保したサイズも合わせて引数渡しすること。(ちなみによくあるライブラリによっては文字列の先頭ポインタとサイズを構造体にしているものも多い。)サイズを引数指定することで、データコピー時のバッファチェックがしやすくなる。

    例2のような引数や復帰値に構造体を使った関数は、関数内で動的確保を行い、呼び元の責任で開放する。構造体の初期化がしやすくなる(と、僕は思ってる)。

  • テストケースを作成しやすいという、テスタビリティ
  • 例3
    int
    func3(
        int c1,
        const char* c2,
        char* c3,
        int c3size )
    {
    /* (A)引数チェック */
        if( c1 <= 0 || c2 == NULL || *c2 == 0x00 || c3 == NULL || c3size <= 0 ){
            return(ERR_ARGV);
        }

    /* (B)本体処理 */

    /* (C)エラー処理 */
    retired:
        /* 事後処理・初期化・開放処理 */
        func_free();
        return(ERR_SYSTEM);
    }

    実装は(A)引数チェック(B)本体処理(C)エラー処理の3部で構成すると、テストケースが作成しやすいのではないかと思っている。(A)引数チェックは必須。ここでのチェックがこの先のテスト空間を限定してくれるから。あるいは、ここに欠陥を紛れ込ませて、テストのテスト(テストが無作為で紛れ込ませた欠陥を検出できるかの検証)ができるかも?引数によっては関数の旅をするものもいるだろうが、
    僕の場合はすべての関数に対してここは必ずテストケースとして設定する。

    (B)実装部分については、割愛。

    (C)C言語で「goto文は使わない」というしきたりがあるかもしれないが、エラー処理については使ったほうがフローが簡潔になるし、開放漏れ・クローズ漏れを起こしにくいんじゃないかと思います。開放漏れ・クローズ漏れはなかなか単体テストでは発見しずらいか。

    てな感じで僕は設計してます。

    ==

    さあ、実装とテストを駆動させるのだが、ご紹介するのは、僕が単体テストで使う3つのツール

    • minunit(自作改良版) - 単体テスト用テストコマンド自動生成
    • minunitというたった3行のユニットテストツール。これをベースにテストコマンド用ソースを自動生成するツールを自作して利用。

    • gcov - プログラムコードカバレッジ測定ツール
    • GCCのプロファイリング機能で「-fprofile-arcs -ftest-coverage」を指定してコンパイルする。詳しくはここで。分岐網羅の測定も可能か。

    • lcov - テストカバレッジ情報グラフィック化
    • gcovで測定した結果をHTMLと画像に変換するツール。詳しくはここ。Linux用だが、Solarisにも移植はできるよ。

    これらのツールについては、もう少し勉強・改良してみたいと思っています。

    | | コメント (0) | トラックバック (0)

    C言語における単体テスト、あるいはTDDについての考察

    今日はあったかかった。というか昼間は暑かった。

    ==

    単体テストといえばjavaのためのJUnitが有名だが、C言語でもCUnitCUnit for Mr.AndoEmbeddedUnitCCUnitなどいくつか世の中に出回っている。

    C言語におけるTDDの問題点と解決方法

    (1) スタブと実体の競合
    他の関数をテストするために作成したスタブと、関数の実体が競合を起こします。

    (中略)

    (2)スタブ同士の競合
    テスト対象の関数に応じたスタブを作成した場合にスタブ同士が競合を起こします。

    ここではスタブの競合という問題を、関数の別名宣言、関数ポインタの設定などの手順を使って回避しています。あるいはコンパイル環境を分ける、という解決法も示されています。

    これって、ボトムアップの単体・結合テストを繰り返すことでも回避できるんじゃないかと思ってるんだけど、どうなんだろう。最下部のコンポーネント(C言語であれば、システムコール標準関数にあたるか)については単体テスト実施済みというスタンスをとれば、ボトムアップの結合テストを繰り返していけば、すべてのコンポーネント(C言語であれば、ユーザ関数)の単体テストとみなせるのでは?という考え方。

    実際に僕がやっている単体テストってこのやり方なのだ。正確には単体テストの進捗具合によって単体(コンポーネント)の単位が大きくなっていくので、見方によっては結合テストともとれるか。

    詳細についてはいずれまた。今日はこれくらいで。


    | | コメント (4) | トラックバック (0)

    ソフトウェアテストPRESS vol.1 - テスト駆動開発と単体テストのカンケイ

    本棚にある雑誌を読み直してて見つけた。

    テスト駆動開発と単体テストのカンケイ by ソフトウェアテストPRESS vol1 (p111)

    さて、ここでTDDで作られるテストコードは、今までコーディング後に単体テストとして実施していたものを、先に書いただけと見ることも可能です。ということは、コーディングが終われば単体テストが終わったことになるのでしょうか?

    これは、半分正しいですが半分間違っています。

    (中略)

    実際のところ、TDDのテストファーストなテストで4〜5の観点のテストコードが漏れなく書かれていることは通常ないでしょう。なぜなら、テストコードを書くための工数が増えすぎて作業のスピード感が一気に落ちてしまい、本来のTDDの良さがなくなってしまうためです。

    引用中の4とは「境界値パターン」、5とは「ゼロ、NULLなどパターン」です。すなわち境界値分析とエラー推測的な観点でのテストケースといえます。そういったテストケースはTDDの場合、スピード感を損なうために通常はテストコードとして実装されず、単体テストとして別枠で工数をとって実施しましょう、という趣旨。

    これについは僕は少し考えが違ってます。というか僕はこの4と5のパターンのテストコードも実装します。指摘されている通り、スピード感が落ちてしまってTDDの良さが消えてしまいそうですが、それでも回帰的/スモーク的なテストができるというメリットはやはり捨てがたい。目的はTDDをすることではなくて、一定の品質を保つ製品をなるべく短期間・低コストで作成することだと思うので、「TDDの良さがなくなってしまうため」に4や5を組み込まないということはしない。

    ただ、前半部分のコーディング終了=単体テスト終了、っていうは間違ってない気がしてきた。何らかの指標があってそれを評価してはじめて単体テストが終了する、といったイメージかな。(テストカバレッジとか)

    # あくまで私見です。そんなにTDDの専門家といえるほどの知識も経験もないので。
    # ちゃんと勉強するためには、こんな本読まなきゃいけないのか。。。汗

    | | コメント (0) | トラックバック (0)

    テスト駆動型開発(TDD)とグレーボックステスト

    最近、テスト駆動型開発(TDDあるいはBDD)についての雑誌を買ったり他のブログに感化されたりしたので軽い考察を。

    ==

    実装とテストを繰り返しながら設計をすすめる、といった形でコーディングするとき、みなさんはどんな手順ですすめますか?僕はこんな感じでやってます。(僕はC言語をベースに開発することが多いので、C言語をサンプルに)

    1. 骨組みだけの関数を作成する


    2. /**
       * func1(); ○△×をする関数
       * 引数
       * 復帰値
       * 0 正常
       * -1 エラー
       */
      int
      func1(
        const char* str1,
        char* ptr2,
        int num3 )
      {
        return(-1);
      }

      モジュール設計に基づいた関数の骨組みだけを記述する。ただし、復帰値がvoid以外ならばエラー復帰をまず記述しておく。


    3. テストスイートを作成する


    4. - 引数チェック
      - 正常系ケース
      - 限界値ケース
      - エラーケース
      - 異常系ケース

      テストスイートは複数のテストケースの集まりをあらわし、「引数チェック」「正常系ケース」「限界値ケース」「エラーケース」「異常系ケース」といったテストケースを作成する。引数チェックは、関数の引数にNULL、0、負の数、ヌル文字のポインタ(””)などを指定した場合に正しく引数エラーを返すかどうかを確認する。正常系ケースでは、尤もらしい動作をするであろう条件を指定する。最低でもひとつ、そしてコーディング後に追加するのもあり。限界値ケースは、条件の境界値(最大値、最小値)において正常な動作をすることを確認する。これは正常系ケースと兼ねてもいい。エラーケースは、尤もらしいエラーが発生する条件を指定し、エラーが発生することを確認するテストケース。異常系ケースは、今までの4群以外で、例えば非常に大きなデータや、言語上の限界値などを指定する。

      これらのテストケースをまとめる際、仕様書の欠陥などを同時にレビューするのもよい。


    5. テストを実行する
    6. 骨組みだけの関数をコンパイルし、テストスイートにある条件&評価でテストする。僕の場合はMinUnitというマクロをベースにテストコードを自動生成して、「-fprofile-arcs -ftest-coverage」をオプションにしてコンパイル&実行する。

      ちなみに、ほとんどテストケースは失敗に終わり、いわゆる「レッド」の状態。



      /* file: minunit.h */
      #define mu_assert(message, test) do { if (!(test)) return message; } while (0)
      #define mu_run_test(test) do { char *message = test(); tests_run++; \
                  if (message) return message; } while (0)
      extern int tests_run;

      -fprofile-arcs -ftest-coverage」をオプションにするのはあとでgcovでカバレッジ測定をするため。


    7. テストケースをクリアするように実装をすすめる
    8. 対象となる関数を実装していき、テストケースが「グリーン」にしていく。このとき、実装とともに必要と思われるテストケースを追加していくのもあり。


    9. テストスイートがクリアすることと、カバレッジ率を80%以上に保つ
    10. テストケースをすべてクリアすることと同時に、ステートメントカバレッジにも着目すること。僕の場合は、目安は80%~95%を目標にする。


    本当は「リファクタリング」も必要なのだが、とりあえずこんな感じで試しているところ。

    ==

    さて、こういったテストファーストで実装していくこと得られるメリットは、というと、、、

    1. ユニットテストケースがグレーボックスっぽい
    2. 境界値や異常系を考慮するので、グレーボックス的なアプローチができる。

    3. 引数チェックでブラックボックスっぽい
    4. 引数チェックは境界値やエラー推測といったブラックボックス的なアプローチができる。

    5. 仕様書の見える化、掘り起こしができる
    6. テストケースを作成するプロセスは、仕様を浮き彫りにする作業となる。

    ただし、いいことずくめではないと思っていて、引数が多くなったり、複雑な処理になるにしたがって、テストケースは膨れ上がる。テストをグリーンにするために実施カバレッジは少なくとも100%にする必要もある。だから、どこかで手を打たなくちゃならない。その基準は、まだよくわからない。


    | | コメント (0) | トラックバック (0)

    勉強会参加できず。

    仕事が長引いて、先週末に開催された社外の勉強会に参加できず。残念。。。

    ==

    テスト駆動型開発(TDD: Test Driven Development または BDD: Behavior Driven Development)は、シンプルさを追求した設計フレームワークである。このキーワードで何か記事を書く予定。

    ちなみにTDDに関する最近の記事。
    http://www.atmarkit.co.jp/fdotnet/nagile/nagile05/nagile05_01.html
    http://blog.goo.ne.jp/wildriver_1977/e/0712f31a16f0c959d7a132136ac40582

    コメントは後ほど。

    | | コメント (0) | トラックバック (0)