28

これは私が知っている難しい自由形式の質問ですが、私はそれを床に投げて、誰かが何か面白い提案があるかどうかを確認したいと思いました。

PythonインターフェイスをC++コード(SWIGを介して生成)に変換し、これをWebServicesとして公開するために必要なコードを生成するコードジェネレーターを開発しました。このコードを開発したとき、TDDを使用して開発しましたが、テストが非常に脆弱であることがわかりました。各テストは基本的に、特定の入力コード(C ++ヘッダー)に対して特定の出力コードを取得することを確認する必要があるため、XML入力ファイルからテスト定義を読み取り、テストを生成する小さなエンジンを作成しました。これらの期待からのケース。

問題は、コードを変更することを恐れていることです。それと、ユニットテスト自体がa:複雑で、b:もろいという事実。

だから私はこの問題への代替アプローチを考えようとしています、そしてそれは私がおそらくそれを間違った方法で取り組んでいることに気づきます。たぶん私は結果にもっと焦点を合わせる必要があります、IE:私が生成したコードは実際に実行され、私が望むように見えるのではなく、私が望むことをしますか?

誰かがこれに似た何かの経験を共有したいと思ったことはありますか?

4

8 に答える 8

13

私は自分のコードジェネレーターでの経験の要約を書き始め、次に戻って質問を読み直しましたが、コードのレイアウト/外観ではなく実行結果に焦点を当てて、同じ問題に自分ですでに触れていることがわかりました。

問題は、これをテストするのが難しいこと、生成されたコードが単体テストシステムの環境で実際に実行するのに適していない可能性があること、そして期待される結果をどのようにエンコードするかです。

コードジェネレーターを細かく分割して、それらを単体テストする必要があることがわかりました。完全なコードジェネレーターの単体テストは、私に言わせれば、単体テストというよりも統合テストに似ています。

于 2008-08-14T14:04:25.800 に答える
5

「単体テスト」はテストの 1 種類にすぎないことを思い出してください。コード ジェネレーターの内部部分を単体テストできるはずです。ここで実際に見ているのは、システム レベルのテスト (別名回帰テスト) です。それはセマンティクスだけではありません...さまざまな考え方、アプローチ、期待などがあります。確かに多くの作業が必要ですが、おそらく弾丸を噛んでエンドツーエンドの回帰テストスイートをセットアップする必要があります:修正されたC++ファイル-> SWIGインターフェイス -> python モジュール -> 既知の出力。既知の入力 (修正された C++ コード) と予想される出力 (最終的な Python プログラムから出力されるもの) を実際にチェックする必要があります。コード ジェネレーターの結果を直接確認することは、オブジェクト ファイルを比較するようなものです...

于 2008-08-14T18:15:42.733 に答える
0

ユニットテストは、特定のユニットをテストすることです。したがって、クラス A の仕様を作成する場合、クラス A にクラス B および C の実際の具体的なバージョンがないことが理想的です。

後で、この質問のタグに C++ / Python が含まれていることに気付きましたが、原則は同じです。

    public class A : InterfaceA 
    {   
      InterfaceB b;

      InterfaceC c;

      public A(InterfaceB b, InterfaceC c)   {
          this._b = b;
          this._c = c;   }

      public string SomeOperation(string input)   
      {
          return this._b.SomeOtherOperation(input) 
               + this._c.EvenAnotherOperation(input); 
      } 
    }

上記のシステム A はシステム B と C にインターフェイスを挿入するため、実際の機能を他のシステムで実行することなく、システム A だけを単体テストできます。これは単体テストです。

以下は、動作の各部分に対して異なる When 仕様を使用して、作成から完成まで System にアプローチするための巧妙な方法です。

public class When_system_A_has_some_operation_called_with_valid_input : SystemASpecification
{
    private string _actualString;

    private string _expectedString;

    private string _input;

    private string _returnB;

    private string _returnC;

    [It]
    public void Should_return_the_expected_string()
    {
        _actualString.Should().Be.EqualTo(this._expectedString);
    }

    public override void GivenThat()
    {
        var randomGenerator = new RandomGenerator();
        this._input = randomGenerator.Generate<string>();
        this._returnB = randomGenerator.Generate<string>();
        this._returnC = randomGenerator.Generate<string>();

        Dep<InterfaceB>().Stub(b => b.SomeOtherOperation(_input))
                         .Return(this._returnB);
        Dep<InterfaceC>().Stub(c => c.EvenAnotherOperation(_input))
                         .Return(this._returnC);

        this._expectedString = this._returnB + this._returnC;
    }

    public override void WhenIRun()
    {
        this._actualString = Sut.SomeOperation(this._input);
    }
}

結論として、単一のユニット/仕様は複数の動作を持つことができ、仕様はユニット/システムを開発するにつれて成長します。また、テスト中のシステムがその中の他の具体的なシステムに依存している場合は注意してください。

于 2010-05-19T23:17:11.220 に答える
0

はい、結果だけが重要です。本当の雑用は、生成されたコードを独立して実行できるようにするフレームワークを作成することです...そこに時間を費やしてください。

于 2008-08-14T14:38:19.630 に答える
0

私の推奨事項は、既知の入出力結果のセット (既に用意されているいくつかの単純なケースなど) を把握し、生成されたコードの単体テストを行うことです。ジェネレーターを変更すると、生成される正確な文字列がわずかに異なる可能性があります...しかし、本当に気にするのは、同じように解釈されるかどうかです。したがって、そのコードが機能である場合にテストするように結果をテストすると、希望する方法でコードが成功するかどうかがわかります。

基本的に、あなたが本当に知りたいのは、考えられるすべての組み合わせを物理的にテストせずに、ジェネレーターが期待どおりの結果を生成するかどうかです (これも不可能です)。ジェネレーターが期待どおりに一貫していることを確認することで、ジェネレーターがますます複雑な状況で成功するという安心感を得ることができます。

このようにして、一連の回帰テスト (正常に動作し続ける必要がある単体テスト) を構築することもできます。これは、ジェネレーターへの変更が他の形式のコードを壊していないことを確認するのに役立ちます。単体テストでキャッチできなかったバグに遭遇した場合、同様の破損を防ぐためにそれを含めることができます。

于 2010-07-25T23:56:06.117 に答える
0

*nux で実行している場合は、bash スクリプトまたは makefile を優先して unittest フレームワークをダンプすることを検討してください。Windows では、ジェネレーターを実行し、コードを (別のプロセスとして) 使用して単体テストを行うシェル アプリ/関数を構築することを検討できます。

3 番目のオプションは、コードを生成し、それから単体テストのみを含むアプリをビルドすることです。繰り返しますが、入力ごとにこれを実行するには、シェルスクリプトなどが必要です。予想される動作をエンコードする方法については、C++ ではなく生成されたインターフェイスを使用するだけで、C++ コードの場合とほぼ同じ方法で実行できることがわかりました。

于 2008-08-14T15:46:01.927 に答える
0

結果を検証しながら、きめの細かいテストを実行できることを指摘したかっただけです。コードの個々のチャンクを、いくつかのセットアップおよび検証コード内にネストすることでテストできます。

int x = 0;
GENERATED_CODE
assert(x == 100);

生成されたコードが小さなチャンクから組み立てられ、チャンクが頻繁に変更されない場合は、より多くの条件を実行してテストを少し改善することができ、1 つのチャンクの詳細を変更したときにすべてのテストが中断することを回避できます。

于 2008-09-16T09:41:03.870 に答える
0

生成方法よりも、何を生成しているかをテストする必要があることがわかりました。

私の場合、プログラムは Web アプリケーションにコンパイルされる多くの種類のコード (C#、HTML、SCSS、JS など) を生成します。回帰バグを全体的に減らす最善の方法は、ジェネレーターをテストするのではなく、Web アプリケーション自体をテストすることです。

誤解しないでいただきたいのですが、ジェネレーター コードの一部をチェックする単体テストはまだありますが、私たちの費用対効果の最大の成果は、生成されたアプリ自体の UI テストです。

これを生成しているので、プログラムでアプリをテストするために使用できる JS で優れた抽象化も生成します。ここで概説されているいくつかのアイデアに従いました: http://code.tutsplus.com/articles/maintainable-automated-ui-tests--net-35089

素晴らしい点は、コード生成から実際に生成しているものまで、システムをエンドツーエンドで実際にテストすることです。テストが失敗すると、ジェネレーターが壊れた場所まで簡単に追跡できます。

それはかなり甘いです。

幸運を!

于 2015-07-20T04:10:25.057 に答える