web-dev-qa-db-ja.com

本当にユニットテストフレームワークが必要ですか?

現在私の仕事では、C++アプリケーション用のユニットテストの大規模なスイートがあります。ただし、単体テストフレームワークは使用しません。基本的には、assertとcoutをラップするCマクロを利用するだけです。何かのようなもの:

VERIFY(cond) if (!(cond)) {std::cout << "unit test failed at " << __FILE__ << "," << __LINE__; asserst(false)}

次に、各テストの関数を作成します。

void CheckBehaviorYWhenXHappens()
{
    // a bunch of code to run the test
    //
    VERIFY(blah != blah2);
    // more VERIFY's as needed
}

私たちのCIサーバーは「ユニットテスト失敗」をピックアップし、ビルドを失敗させ、開発者にメッセージを電子メールで送信します。

また、セットアップコードが重複している場合は、本番環境で使用する他の重複コードと同じように単純にリファクタリングします。私たちはそれをヘルパー関数の後ろにラップし、いくつかのテストクラスが頻繁に使用されるシナリオの設定をラップするようにします。

CppUnitやboost単体テストのようなフレームワークがあることは知っています。これらはどのような価値をもたらすのでしょうか。これらがテーブルにもたらすものを見逃していますか?それらから私が得ることができる何か有用なものはありますか?特に私たちが持っているものは非常に単純でうまく機能しているように見えるので、それが実際の価値を追加しない限り、依存関係を追加することをためらっています。

19
Doug T.

他の人がすでに言ったように、あなたはすでにあなた自身のシンプルな自家製フレームワークを持っています。

作るのは簡単なようです。ただし、単体テストフレームワークには、言語に関する高度な知識を必要とするため、実装がそれほど簡単ではない機能がいくつかあります。私がテストフレームワークに通常必要とし、自作するのがそれほど簡単ではない機能は次のとおりです。

  • テストケースの自動収集。つまり新しいテストメソッドを定義するだけで、それを実行できます。 JUnitは、名前がtestで始まるすべてのメソッドを自動的に収集し、NUnitは[Test]アノテーション、Boost.TestはBOOST_AUTO_TEST_CASEおよびBOOST_FIXTURE_TEST_CASEマクロ。

    これはほとんどの場合便利ですが、開発者が実際に必要なテストを記述し、正しく接続する可能性が高まるので、便利です。長い指示があると、誰かがそれらの一部を見逃してしまい、おそらくいくつかのテストが実行されず、だれも気付かないでしょう。

  • コードを調整したり再コンパイルしたりせずに、選択したテストケースを実行する機能。適切な単体テストフレームワークでは、コマンドラインで実行するテストを指定できます。単体テストでデバッグしたい場合(多くの開発者にとってそれらの中で最も重要な点です)、コードをいたるところに微調整することなく、実行するものだけを選択できる必要があります。

    バグレポート#4211を受け取ったばかりで、単体テストで再現できるとします。そのため、1つ記述しますが、ランナーにそのテストだけを実行するように指示する必要があるので、実際に何が問題なのかをデバッグできます。

  • チェック自体を変更せずに、テストケースごとに予想される失敗をテストにマークする機能。実際にフレームワークを切り替えてこれを取得しました。

    適切なサイズのテストスイートには、テストが含まれます。テストする機能がまだ実装されていない、まだ完了していない、まだ修正する時間がないなどの理由で失敗しています。テストを予期された失敗としてマークする機能がないと、定期的にいくつかの失敗が発生しても別の失敗に気付かないため、テストの主な目的の機能が停止します。

8
Jan Hudec

あなたはすでにフレームワーク、自家製のものを使用しているようです。

より一般的なフレームワークの付加価値は何ですか?彼らが追加する価値は、社外の人々とコードを交換しなければならないときに、それができるということです既知で広く使用されているであるフレームワークに基づいているためです。

一方、自家製のフレームワークでは、コードを共有しないか、フレームワーク自体を提供する必要があり、フレームワーク自体の成長に伴って煩雑になる可能性があります。

説明もユニットテストフレームワークもないまま、コードをそのまま同僚に渡すと、彼はそれをコンパイルできません。

自家製フレームワークの2番目の欠点は互換性です。一般的な単体テストフレームワークは、さまざまなIDE、バージョン管理システムなどとの互換性を確保する傾向があります。現時点では、それほど重要ではないかもしれませんが、CIサーバーで何かを変更するか、移行する必要がある場合に何が起こるか新しいIDEまたは新しいVCSに?ホイールを再発明しますか?

最後に重要なことですが、より大きなフレームワークはその他の機能を提供します。自分のフレームワークにいつか実装する必要があるかもしれません。 Assert.AreEqual(expected, actual)は必ずしも十分ではありません。必要な場合:

  • 精度を測定しますか?

    Assert.AreEqual(3.1415926535897932384626433832795, actual, 25)
    
  • 実行時間が長すぎるかどうかをテストしますか?タイムアウトの再実装は、非同期プログラミングを容易にする言語であっても簡単ではない場合があります。

  • 例外がスローされることを期待するメソッドをテストしますか?

  • よりエレガントなコードがありますか?

    Assert.Verify(a == null);
    

    大丈夫ですが、代わりに次の行を書く意図の方が表現力がありませんか?

    Assert.IsNull(a);
    
28

他の人がすでに言ったように、あなたはすでにあなた自身の自家製フレームワークを持っています。

他のテストフレームワークを使用するために私が見ることができる唯一の理由は、業界の「常識」の観点からでしょう。新しい開発者は、自分で作った方法を学ぶ必要はありません(ただし、非常にシンプルに見えます)。

また、他のテストフレームワークには、利用できるより多くの機能がある場合があります。

4
ozz

単純なフレームワークであっても、すでにフレームワークを持っています。

私が見るより大きなフレームワークの主な利点は、さまざまな種類のアサーション(レイズのアサートなど)、ユニットテストの論理的な順序、およびユニットテストのサブセットのみを実行できることです。時間。また、xUnitテストのパターンは、できれば従うのがかなり良いです。たとえば、setUP()やtearDown()などです。もちろん、それはあなたを上記のフレームワークに閉じ込めます。一部のフレームワークは、他のフレームワークよりもモック統合が優れていることに注意してください。たとえば、Googleモックとテストです。

すべての単体テストを新しいフレームワークにリファクタリングするのにどのくらいかかりますか?数日または数週間は価値があるかもしれませんが、それ以上ではないかもしれません。

私の見方では、あなたは両方とも有利であり、あなたは「不利」(原文のまま)です。

利点は、あなたが快適に感じ、あなたのために働くシステムを持っているということです。製品の有効性を確認できて嬉しいです。また、別のフレームワークを使用するもののすべてのテストを変更しようとしても、ビジネス上の価値はないでしょう。コードをリファクタリングでき、テストが変更を反映する場合-さらに良いことに、テストを変更でき、既存のコードがリファクタリングされるまでテストに失敗した場合、すべてのベースがカバーされます。しかしながら...

適切に設計された単体テストAPIを使用する利点の1つは、ほとんどの最新のIDEに多くのネイティブサポートがあることです。これは、ハードコアVIに影響を与えることはなく、Visual Studioユーザーをあざ笑うemacsユーザーもいますが、優れたIDEを使用するユーザーにとっては、テストをデバッグして実行することができます。 IDE自体。これは良いことですが、使用するフレームワークによってはさらに大きな利点があり、それは使用される言語にあります)コードをテストします。

languageと言うとき、私はプログラミング言語について話しているのではなく、代わりに、テストコードを次のように読めるようにする流暢な構文に包まれた豊富なセットの単語について話している物語。特に、私は [〜#〜] bdd [〜#〜] フレームワークの使用を提唱しています。私の個人的なお気に入りのDotNet [〜#〜] bdd [〜#〜] APIは StoryQ ですが、概念を理解するという同じ基本的な目的を持つ他のいくつかがあります要件ドキュメントから、仕様での記述方法と同様の方法でコードに記述します。ただし、非常に優れたAPIは、テスト内のすべての個々のステートメントをインターセプトし、そのステートメントが正常に実行されたか失敗したかを示すことにより、さらに前進します。これは非常に便利です。早期に戻ることなくテスト全体が実行されたことを確認できるため、呼び出し全体をデコードする必要がなく、失敗したテストの部分にのみ注意を向ける必要があるため、デバッグ作業が非常に効率的になります。シーケンス。もう1つのいい点は、テスト出力がこの情報のすべてをコンソールといくつかのAPIの両方で表示することです。このようなものに興味がある場合は、管理者に与えることができます。

私が話していることの例として、以下を比較してください:

アサートの使用:

Assert(variable_A == expected_value_1); // if this fails...
Assert(variable_B == expected_value_2); // ...this will not execute
Assert(variable_C == expected_value_3); // ...and nor will this!

流暢なBDD APIの使用:(斜体のビットは基本的にメソッドポインターであると想像してください)

WithScenario("Test Scenario")
    .Given(*AConfiguration*) // each method
    .When(*MyMethodToTestIsCalledWith*, variable_A, variable_B, variable_C) // in the
    .Then(*ExpectVariableAEquals*, expected_value_1) // Scenario will
        .And(*ExpectVariableBEquals*, expected_value_2) // indicate if it has
        .And(*ExpectVariableCEquals*, expected_value_3) // passed or failed execution.
    .Execute();

BDD構文が長くなり、より幅広で、これらの例はひどく工夫されていますが、特定のシステム動作の結果としてシステムで多くのものが変更される非常に複雑なテスト状況では、BDD構文により明確な情報が得られますテストの内容とテスト構成の定義方法に関する説明。プログラマー以外の人にこのコードを見せれば、何が起こっているのかすぐに理解できます。さらに、両方のケースで「variable_A」がテストに失敗した場合、問題が修正されるまでAssertsサンプルは最初のアサートを超えて実行されませんが、BDD APIはチェーンで呼び出されたすべてのメソッドを順番に実行し、ステートメントの個々の部分にエラーがありました。

個人的には、このアプローチは、テストの言語が顧客が論理的な要件について話すのと同じ言語であるという意味で、従来のxUnitフレームワークよりもはるかにうまく機能することがわかります。それでも、私の取り組みをサポートするための完全なテストAPIを開発する必要なく、xUnitフレームワークを同様のスタイルで使用することができました。アサートは依然として効果的に短絡しますが、よりきれいに読みます。例えば:

Nunitの使用

[Test]
void TestMyMethod()
{
    const int theExpectedValue = someValue;

    GivenASetupToTestMyMethod();

    var theActualValue = WhenIExecuteMyMethodToTest();

    Assert.That(theActualValue, Is.EqualTo(theExpectedValue)); // Nice, but it's not BDD
}

単体テストAPIの使用を検討する場合は、しばらくの間、多数の異なるAPIを実験し、アプローチについて心を開いておくことをお勧めします。私は個人的にBDDを提唱していますが、あなた自身のビジネスニーズでは、チームの状況に応じて別のものが必要になる場合があります。ただし、重要なのは、既存のシステムの再推測を回避することです。必要に応じて、別のAPIを使用したいくつかのテストで既存のテストをいつでもサポートできますが、すべてを同じにするためだけに大規模なテストを書き直すことはお勧めしません。レガシーコードが使用されなくなると、そのコードとそのテストを新しいコードに簡単に置き換え、代替APIを使用してテストを行うことができます。これにより、必ずしも実際のビジネス価値をもたらすわけではない大きな労力に投資する必要がなくなります。単体テストAPIの使用に関しては、構文でNice自然言語の使用が許可されていて、さらに優れている場合、フレームワークでコーディング/デバッグ作業を効率化できれば、おそらく、適切な単体テストAPI。

2
S.Robins

あなたが持っているのは簡単で、仕事を成し遂げます。それがあなたのために働くなら、素晴らしい。 needは主流のユニットテストフレームワークではありません。ユニットテストの既存のライブラリを新しいフレームワークに移植する作業に遠慮します。ユニットテストフレームワークの最大の価値は、参入障壁を減らすことです。フレームワークがすでに用意されているので、テストを書き始めるだけです。あなたはその時点を過ぎているので、その利益を得ることはできません。

主流のフレームワークを使用することのもう1つの利点は、それがマイナーな利点であるIMOです。これは、新しい開発者が、使用しているフレームワークに既に慣れているため、トレーニングが少なくて済むことです。実際には、これまでに説明したような単純なアプローチでは、これは大した問題ではありません。

また、ほとんどの主流のフレームワークには、フレームワークが持つ機能とそうでない機能があります。これらの機能により、配管のコードが削減され、テストケースの作成がより速く簡単になります。

  • 命名規則、注釈/属性などを使用したテストケースの自動実行.
  • より具体的なさまざまなアサート。すべてのアサーションの条件付きロジックを記述したり、例外をキャッチしてタイプをアサートしたりする必要はありません。
  • テストケースの分類。これらのサブセットを簡単に実行できます。
1
Adam Jaskiewicz