Build Status | Master | Dev |
---|---|---|
A super low friction specification framework based on the fantastic Fixie test framework.
You might ask: Why create another specification framework? There are so many good ones already. And you would be right. But I always have the feeling I have to do a lot for those frameworks until they start to do something for me. Your mileage might vary depending on the chosen framework. I tried a lot of them believe me.
Either you have to use code generation (not a bad thing in itself, but friction), play some tricks with lambdas, being committed to a certain assertion library, or try to mimic Ruby frameworks (a lot of good ideas over there, but some things do not translate to c# without damage)
Make no mistake here:
There are advantages and disadvantages for all approaches. So this approach has also not only advantages.
It is an opinionated framework for a start.
-
Super low friction specification authoring
But that comes not for free. You have to follow a naming convention for your specifications. More on that later on. -
Minimal ceremony
Specifications are picked up automatically by using the Fixie test framework for specification execution.Fixie
is much more open to different styles of testing than fixieSpec. -
One test class per scenario
I strongly believe in independent unit tests (FIRST principle), however when it comes to specifications I value other traits higher. I very much favour a test class per scenario life cycle here. This allows to setup a context, execute some transitions (exercising the system under test) followed by one or more assertions. The different scenario classes should still be as independent from each other as possible. -
From nothing to a specification in 10 minutes
That is a bold statement, but after following the "Getting started" guide, you decide if it is true or not.
To install released versions of fixieSpec
via NuGet, run the following command in the Package Manager Console:
PM> Install-Package fixieSpec
To install develop versions of fixieSpec
from Myget, you need to add the following url as package source
https://www.myget.org/F/fixiespecdev/api/v2
After that, run the following command in the Package Manager Console:
PM> Install-Package fixieSpec
A specification is a simple public class like the following from the Sample application:
public sealed class AudioRecordingSucceedsWithMicrophone
{
readonly Microphone microphone;
readonly AudioRecording audioRecording;
public AudioRecordingSucceedsWithMicrophone(
AudioRecording anAudioRecording,
Microphone aMicrophone)
{
audioRecording = anAudioRecording;
microphone = aMicrophone;
}
public void Given_a_microphone_is_available()
{
microphone.MakeAvailable();
}
public void When_the_audio_recording_is_started()
{
audioRecording.StartRecording(microphone);
}
public void Then_the_audio_recording_should_be_recording()
{
audioRecording.ShouldBeRecording();
}
public void And_then_the_selected_microphone_is_used_for_recording()
{
microphone.ShouldBeRecording(audioRecording);
}
public void And_then_the_selected_microphone_is_not_available_anymore()
{
microphone.IsAvailable().ShouldBeFalse();
}
}
As you can see this is a simple class with no frills, but a lot is happening in the background:
- The specification class is found by convention
- The class is instantiated and its constructor parameters are resolved from somewhere
- A context setup step is executed after the class has been constructed
- A scenario specific transition step is executed on the SUT (system under test)
- Multiple assertion steps are executed in the order of their declaration
How is that done? If you follow the next steps, then you will understand how all that magic happens.
Fixie is a conventional test framework and so is fixieSpec
. So it makes sense to consult the Fixie documentation before continuing.
Are you back? Fine. Now you know about the power of conventions and that is important, because fixieSpec
leverages on that. Fixie
allows to apply multiple conventions at the same time by using the TestAssembly class. But it is only useful when combining conventions that are not applied to the same set of tests or specifications for that matter.
Since fixieSpec
has an opinion about test case naming and test class life cycle, its conventions need to be applied. To do that you need to create your own convention and inherit that from the FixieSpecConvention
class.
The FixieSpecConvention
makes the following assumptions about how to write specification:
- A specification class per scenario
- Specifications are broken down into simple steps
- There are different types of steps
- Context setup steps that establish the context of the specification scenario
- Transition steps that cause observable effects on the SUT or its dependencies
- Assertion steps that verify direct output of the SUT, the state of the SUT or observe indirect output of the SUT (e.g. via the dependencies of the SUT)
- An instance is created per specification class (The specification class is created and then all steps are executed in the order of declaration))
- Specification context setup that is not relevant for the scenario happens in the constructor
- Optionally primary scenario specific context setup happens in a
void
method namedGiven..
- Optionally further scenario specific context setup happens in
void
methods namedAnd_given...
- The primary transition of the SUT (the exercising step of the scenario) happens in a
void
method namedWhen_..
- Optional further transition of the SUT happens in
void
methods namedAnd_when...
- The primary assertion (the verification) happens in a
void
method namedThen_...
- Optional further assertions happen in
void
methods namedAnd_then...
- Cleanup happens in an optional
Dispose
method (You have to implementIDisposable
then)
The FixieSpecConvention
makes no assumptions about:
- The casing of you specification methods
- Usage of underscores in your specification methods
- How you name your specification classes
- In which name spaces your specification classes are
- How your specification classes are instantiated.
The following convention class from the Sample application shows how to establish you own conventions:
public class SpecificationConvention : FixieSpecConvention
{
public SpecificationConvention()
{
Classes
.Where(type => type.HasOnlyDefaultConstructor() || type.HasOnlyParameterConstructor());
ClassExecution
.UsingFactory(CreateFromFixture);
}
object CreateFromFixture(Type type)
{
var fixture = new Fixture();
var instance = new SpecimenContext(fixture).Resolve(type);
return instance;
}
}
-
Specifications are limited to classes with either a default constructor or a constructor with parameters. The base
FixieSpecConvention
makes no assumption about the names of specification classes or how the are instantiated. As a user offixieSpec
you can limit the number of classes that are considered as specifications. You can use all the possibilities ofFixie
for that. -
Specification instance creation happens through the
CreateFromFixture
factory method. More on that later on. -
The
CreateFromFixture
factory method is usingAutoFixture
to create specification class instances. More about that later on.
Copyright by Paul Eichenberger, who contributed it. I like how it combines a fixie bike (having one wheel only) with a checklist.