web-dev-qa-db-ja.com

組み込みC開発者向けの優れた単体テストの例

来週は、ユニットテストとテスト駆動開発について、私の部署と話し合う予定です。この一環として、最近作成したコードから実際の例をいくつか紹介しますが、トークで作成する非常に単純な例もいくつか紹介したいと思います。

私はウェブで良い例を探していましたが、私たちの開発分野に特に当てはまるものを見つけるのに苦労していました。私たちが作成するほとんどすべてのソフトウェアは、小型のマイクロコントローラーで実行される深く埋め込まれた制御システムです。 「下」のレイヤーに直接触れない限り、単体テストに簡単に適用できるCコードがたくさんあります(ここでは、ターゲット自体ではなくPCでの単体テストについて話します)。直接話し合うものマイクロコントローラ周辺機器に。しかし、私が見つけたほとんどの例は文字列処理に基づいている傾向があります(たとえば、優れたDive Into Pythonローマ数字の例))。文字列をほとんど使用しないため、これは本当に適切ではありません(私たちのコードが通常使用するライブラリ関数はmemcpymemcmpおよびmemsetだけなので、strcatまたは正規表現に基づくものは正しくありません) 。

それで、質問に:ライブセッションでユニットテストを示すために使用できる関数の良い例を誰かに提供できますか?私の(変更される可能性がある)意見での良い答えは、おそらく次のようになります。

  • 誰でも(たまにコードを書くだけの人でも)理解できるほど単純な関数。
  • 意味がないように見えない関数(つまり、パリティまたはCRCを計算することは、2つの数値を乗算してランダムな定数を追加する関数よりもおそらく良いでしょう)。
  • 部屋の前で書くのに十分短い関数(エラーを減らすためにVimの多くのクリップボードを利用するかもしれません...);
  • 数値、配列、ポインタ、または構造体をパラメータとして取り、文字列を処理するのではなく、類似のものを返す関数。
  • 単純なエラーがある関数(例:> のではなく >=)これは簡単に入れることができますが、ほとんどの場合は機能しますが、特定のEdgeケースでは機能しません。ユニットテストで簡単に識別して修正できます。

何かご意見は?

おそらく関連性はありませんが、テスト自体はおそらくGoogleテストフレームワークを使用してC++で記述されます。すべてのヘッダーにはすでに#ifdef __cplusplus extern "C" {それらの周りのラッパー。これは、これまでに行ったテストでうまく機能しています。

21
DrAl

lenバイトでチェックサムを生成することになっている単純な関数を次に示します。

int checksum(void *p, int len)
{
    int accum = 0;
    unsigned char* pp = (unsigned char*)p;
    int i;
    for (i = 0; i <= len; i++)
    {
        accum += *pp++;
    }
    return accum;
}

これにはフェンスポストのバグがあります。forステートメントでは、テストはi < len

楽しいのは、このようなテキスト文字列に適用すると...

char *myString = "foo";
int testval = checksum(myString, strlen(myString));

あなたは「正しい答え」を得るでしょう!これは、チェックサムされた余分なバイトがゼロ文字列ターミネーターだったためです。したがって、このチェックサム関数をコードに組み込んで、場合によっては出荷して、問題に気付かないようにすることができます。つまり、テキスト文字列以外の何かに適用し始めるまでは。

これは、このバグにフラグを付ける簡単な単体テストです(ほとんどの場合... :-)

void main()
{
    // Seed the random number generator
    srand(time(NULL));

    // Fill an array with junk bytes
    char buf[1024];
    int i;
    for (i = 0; i < 1024; i++)
    {
        buf[i] = (char)Rand();
    }

    // Put the numbers 0-9 in the first ten bytes
    for (i = 0; i <= 9; i++)
    {
        buf[i] = i;
    }

    // Now, the unit test. The sum of 0 to 9 should
    // be 45. But if buf[10] isn't 0 - which it won't be,
    // 255/256 of the time - this will fail.
    int testval = checksum(buf, 10);
    if (testval == 45)
    {
        printf("Passed!\n");
    }
    else
    {
        printf("Failed! Expected 45, got %d\n", testval);
    }
}
15
Bob Murphy

bubble sort のようなソート関数の実装についてはどうですか? sort関数が機能するようになったら、ユニットテストとTDDを導入するのと同じくらい良い binary search を続行できます。

並べ替えと検索は、間違ってしまいがちな比較に依存しています。また、ポインタの交換も必要です。ポインタの交換には注意が必要です。どちらもエラーが発生しやすいので、ごちゃごちゃしてください:)

さらにいくつかのアイデア:

  • 単体テストは、リファクタリングを行うときに非常に役立ちます。したがって、バブルソートが機能したら、qsortのようなより強力なソートに変更できます。テストは引き続きパスし、新しいソート関数も機能することを証明します。
  • ソートはテストが簡単で、結果はソートされるかそうでないかのどちらかであり、これは良い候補です。
  • 検索についても同じです。存在するか存在しないかのどちらかです。
  • ソート用のテストを作成すると、テストに使用する入力のタイプ(ゼロ要素、ランダム入力、重複エントリ、巨大な配列など)などの議論が始まります。
2
Martin Wickman