How to write general tests for all implementations of the interface with MSpec?

I have an IAudioProcessor interface with a single IEnumerable<Sample> Process(IEnumerable<Sample> samples) method. Although this is not a requirement of the interface itself, I want to make sure that all my implementations follow some general rules, for example, for example:

  • Use deferred execution
  • Do not change the original samples.

It’s easy to create tests for them, but I would have to copy and paste these tests for each implementation. I would like to avoid this.

I would like to do something like this (note the GenericTest attribute and the type parameter):

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

Is this possible?

+8
c # unit-testing bdd mspec
source share
2 answers

I am sure that you are looking for Behavior (see also article line test with article behavior ). You define the behavior that each implementation must satisfy ( It fields) in a special class that share SUT and supporting fields (if necessary).

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

Each of your implementations must state that they behave like this special class. You already had a rather complex base class with general setup and behavior, so I will use it (I prefer a simpler, more explicit setup).

 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 _; } 

Your base class defines the general setting (capturing the context enumeration), behavior (processing using a specific impl using a type parameter), and even declaring a behavior field (again, thanks to the generic type parameter, this will be done for each specific one).

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

In addition, you will get output for each of the It fields for each of the implementation classes. Thus, your context / specification reports will be completed.

+2
source share

I Binged MSpec paramaterised tests for you: http://groups.google.com/group/machine_users/browse_thread/thread/8419cde3f07ffcf2?pli=1

Until it appears as a separate green / red test, I don’t think there is anything stopping you from listing the sequence of factories from your separate specification and approving the behavior for each implementation. This will mean that your test will fail even if one implementation fails, but if you want to parameterize, you can try a more free set of tests, for example NUnit.

Edit 1: I'm not sure if MSpec supports the detection of inherited fields to determine the specification, but if so, then at least it should minimize the number of "duplicate" codes without the possibility of using the attribute:

 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> {} 
0
source share

All Articles