8

IAudioProcessor私は単一のメソッドを持つインターフェースを持っていますIEnumerable<Sample> Process(IEnumerable<Sample> samples)。インターフェイス自体の要件ではありませんが、すべての実装が次のようないくつかの一般的なルールに従っていることを確認したいと思います。

  1. 遅延実行を使用する
  2. 入力サンプルを変更しないでください

これらのテストを作成するのは難しくありませんが、実装ごとにこれらのテストをコピーして貼り付ける必要があります。それは避けたいと思います。

私はこのようなことをしたいと思います(属性GenericTestとタイプパラメーターに注意してください):

[GenericTest(typeof(AudioProcessorImpl1Factory))]
[GenericTest(typeof(AudioProcessorImpl2Factory))]
[GenericTest(typeof(AudioProcessorImpl3Factory))]
public class when_processed_audio_is_returned<TSutFactory>
    where TSutFactory : ISutFactory<IAudioProcessor>, new()
{
    static IAudioProcessor Sut = new TSutFactory().CreateSut();
    protected static Context _ = new Context();

    Establish context = () => _.Original = Substitute.For<IEnumerable<ISample>>();

    Because of = () => Sut.Process(_.Original);

    It should_not_have_enumerated_the_original_samples = () =>
    {
        _.Original.DidNotReceive().GetEnumerator();
        ((IEnumerable)_.Original).DidNotReceive().GetEnumerator();
    };
}

このようなことは可能ですか?

4

2 に答える 2

2

ビヘイビアーを探していると確信しています(ビヘイビアーを使用したこの行テストの記事も参照してください)。SUTとサポートフィールド(必要に応じて)を共有する特別なクラスで、すべての実装が満たす必要のある動作(Itフィールド)を定義します。

[Behaviors]
public class DeferredExecutionProcessor
{
    It should_not_have_enumerated_the_original_samples = () =>
    {
        _.Original.DidNotReceive().GetEnumerator();
        ((IEnumerable)_.Original).DidNotReceive().GetEnumerator();
    };

    protected static Context _; 
}

各実装は、この特別なクラスのように動作することを宣言する必要があります。セットアップと動作が共有されたかなり複雑な基本クラスがすでにあるので、それを使用します(より単純で、より明示的なセットアップを好みます)。

public abstract class AudioProcessorContext<TSutFactory>
    where TSutFactory : ISutFactory<IAudioProcessor>, new()
{
    // I don't know Behaves_like works with field initializers
    Establish context = () => 
    {
        Sut = new TSutFactory().CreateSut();

        _ = new Context();
        _.Original = Substitute.For<IEnumerable<ISample>>();
    }

    protected static IAudioProcessor Sut;
    protected static Context _;
}

基本クラスは、共通のセットアップ(コンテキスト列挙をキャプチャする)、動作(typeパラメーターを介して特定のimplで処理する)、さらには動作フィールドを宣言する(ここでも、ジェネリック型パラメーターのおかげで、これはすべてのコンクリートに対して実行されます)を定義します)。

[Subject("Audio Processor Impl 1")]
public class when_impl1_processes_audio : AudioProcessorContext<AudioProcessorImpl1Factory>
{
    Because of = () => Sut.Process(_.Original);
    Behaves_like<DeferredExecutionProcessor> specs;
}

[Subject("Audio Processor Impl 2")]
public class when_impl2_processes_audio : AudioProcessorContext<AudioProcessorImpl2Factory>
{
    Because of = () => Sut.Process(_.Original);
    Behaves_like<DeferredExecutionProcessor> specs;
}

[Subject("Audio Processor Impl 3")]
public class when_impl3_processes_audio : AudioProcessorContext<AudioProcessorImpl3Factory>
{
    Because of = () => Sut.Process(_.Original);
    Behaves_like<DeferredExecutionProcessor> specs;
}

さらに、It各実装クラスの各フィールドの出力を取得します。これで、コンテキスト/仕様レポートが完成します。

于 2011-11-16T05:52:15.883 に答える
0

私はあなたのためにMSpecのパラメータ化されたテストをビンジしました:) http://groups.google.com/group/machine_users/browse_thread/thread/8419cde3f07ffcf2?pli=1

個別の緑/赤のテストとして表示されることはありませんが、単一の仕様内から一連のファクトリを列挙し、各実装の動作をアサートすることを妨げるものは何もないと思います。1 つの実装が失敗してもテストは失敗することを意味しますが、パラメーター化が必要な場合は、NUnit のような緩いテスト スイートを試すことができます。

編集 1: MSpec が仕様を決定するために継承されたフィールドの検出をサポートしているかどうかはわかりませんが、もしそうであれば、属性を使用できずに「繰り返される」コードの量を少なくとも最小限に抑える必要があります。

private class base_when_processed_audio_is_returned<TSutFactory>
    where TSutFactory : ISutFactory<IAudioProcessor>, new()
{
    static IAudioProcessor Sut = new TSutFactory().CreateSut();
    protected static Context _ = new Context();

    Establish context = () => _.Original = Substitute.For<IEnumerable<ISample>>();

    public Because of = () => Sut.Process(_.Original);

    public It should_not_have_enumerated_the_original_samples = () =>
    {
        _.Original.DidNotReceive().GetEnumerator();
        ((IEnumerable)_.Original).DidNotReceive().GetEnumerator();
    };
}

public class when_processed_audio_is_returned_from_AudioProcessorImpl1Factory()
  : base_when_processed_audio_is_returned<AudioProcessorImpl1Factory>
{}

public class when_processed_audio_is_returned_from_AudioProcessorImpl2Factory()
  : base_when_processed_audio_is_returned<AudioProcessorImpl2Factory>
{}
于 2011-11-15T12:51:21.427 に答える