繁体   English   中英

如何为使用MSpec的接口的所有实现编写通用测试?

[英]How do I write generic tests for all implementations of an interface with MSpec?

我有一个具有单个方法IEnumerable<Sample> Process(IEnumerable<Sample> samples)的接口IAudioProcessor 尽管这并不是接口本身的要求,但我想确保所有实现都遵循一些通用规则,例如:

  1. 使用延迟执行
  2. 不要更改输入样本

为这些创建测试并不难,但是我必须为每个实现复制并粘贴这些测试。 我想避免这种情况。

我想做这样的事情(注意属性GenericTest和type参数):

[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();
    };
}

这样的事情可能吗?

我很确定您正在寻找“ 行为” (另请参阅有关行为的 行测试文章 )。 您将在共享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处理),甚至声明了behaviors字段(同样,由于有了泛型type参数,此操作将在每种具体情况下运行) )。

[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字段的输出。 因此,您的上下文/规格报告将是完整的。

我为您测试了MSpec的参数测试:) http://groups.google.com/group/machine_users/browse_thread/thread/8419cde3f07ffcf2?pli=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>
{}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM