Integration with NUnit
AutoFixture offers a glue library for NUnit 3.0. In AutoFixture's lingo, a glue library is a library that extend one library (in this case NUnit 3.0) with AutoFixture.
By using this library, developers can speed up the process of creating unit tests and, while doing so, creating unit tests that are easier to read.
The NUnit glue library is contained in the AutoFixture.NUnit3
NuGet package and is composed by classes that link AutoFixture into NUnit testing pipeline.
The AutoData
and InlineAutoData
attributes sit at the center of the integration of AutoFixture with the NUnit pipeline. They take advantage of the same subsystem used to support parameterized tests and extend it to feed anonymous objects as test parameters.
AutoData
attribute
AutoData
attributeThe AutoData
attribute is used to automatically generate values to be passed to the unit test, effectively making unit test authoring much faster.
Here is an example of a test written without taking advantage of the AutoData
attribute.
By using the AutoData
attribute, the unit test above can be converted as follows.
There are a few noticeable changes between the two snippets:
There is no Arrange phase.
The
AutoData
attribute decorator has been added the unit test.There are new parameters passed to the unit test.
In fact, all of these changes are connected. By adding the AutoData
attribute we have effectively moved the Arrange phase out of the unit test and we now accept all the pieces we need to run the test as parameters.
The main advantages of doing so are:
shorter tests that are easier to scan and understand
focus on the Act and Assert phases rather than on Arrange
centralized configuration on how to create objects (see below)
Specifically, the AutoData
attribute took care of
creating an instance of
Fixture
,inspecting the parameters of the unit test
for each parameter
use the fixture instance to generate a value
pass the generated value to NUnit
InlineAutoData
attribute
InlineAutoData
attributeWhile AutoData
takes care of providing all parameters, InlineAutoData
gives the developer the possibility to specify some of the parameters and takes care of generating the ones whose value was not specified.
Here is an example with the InlineAutoData
attribute.
As shown above, unlike the AutoData
attribute, the InlineAutoData
attribute can be applied more than once on the same unit test. Each instance of the attribute will generate a new execution of the unit test.
Specifically, the following executions will be performed by NUnit
Add_returns_sum_of_parameters(1, <int>, <Service>)
Add_returns_sum_of_parameters(1, 10, <Service>)
Here is how the InlineAutoData
works
for each instance of the
InlineAutoData
creates an instance of
Fixture
inspects the parameters of the unit test
for each parameter
if a value was provided in the constructor of the attribute, pass it to NUnit
otherwise, use the fixture instance to generate a value and pass it to NUnit
It is important to note that the InlineAutoData
attribute suffers of the same restrictions of the TestCase
attribute regarding which types can be used in the attribute. Unfortunately, unlike the TestCase
attribute, the InlineAutoData
attribute doesn't have an equivalent attribute pointing at a method like the TestCaseSource
attribute.
Frozen
attribute
Frozen
attributeAs shown above, the AutoData
and InlineAutoData
can be delegated the creation of the system under test. Doing so creates an interesting challenge if the system under test accepts dependencies that need to be configured or used during the Assert phase.
Let's take the following class as test subject and assume we want to rewrite the unit test using the AutoData
attribute.
Unfortunately, simply decorating the test with the AutoData
attribute and convert the variables list
, sut
and item
as parameters will not work because the list served as parameter and the one served to the constructor of Service
will not be the same instance.
The Frozen
attribute solves this issue. By leveraging the Freeze
extension method, it creates an instance of a given type and it uses it to serve successive requests. This attribute is used by decorating which parameter of the unit test needs to be generated using the Freeze
method and it works with test decorated by both the AutoData
and InlineAutoData
attributes.
With the help of the Frozen
attribute, we can rewrite the test as follow
Please notice the sequence of parameters of the rewritten unit test: the frozen parameters must go before the class using them.
Greedy
and Modest
attributes
Greedy
and Modest
attributesAnother issue deriving from delegating AutoFixture of instantiating classes like system under tests is the loss of control on which constructor is picked. By default, AutoFixture picks the constructor with least parameters, but sometimes this is not the optimal choice.
The Greedy
and Modest
attributes give the developer the power to instruct AutoFixture which constructor to select when constructing an object to be passed to NUnit. The Greedy
attribute will instruct AutoFixture to use the constructor with the most parameters while the Modest
attribute will instruct AutoFixture to follow the default strategy and pick the constructor with least arguments. In both cases, copy constructors (constructors accepting the same type as single parameter) will be ignored.
The Greedy
attribute comes in hand when a service exposes a parameterless constructor with defaults that are not suited for testing.
Here is an example showing when to use the Greedy
attribute.
If neither Greedy
and Modest
are able to select the desired constructor, developers will have to instruct AutoFixture via a customization.
Customizing the fixture
Because the AutoData
and InlineAutoData
attributes encapsulate the generation of parameter values, the test author does not need to use an instance of Fixture
directly making test authoring for common cases quick and trivial.
Unfortunately, doing so prevents the developer from registering customizations and behaviors.
This can be worked around by subclassing the AutoData
and InlineAutoData
attributes.
Here is an example of a basic customization of the two attributes.
With these attribute in place, it is possible to create lean tests without giving up on the customization capabilities of AutoFixture by simply using the new attributes instead of AutoData
and InlineAutoData
.
Dealing with multiple customizations
A problem with this approach is that unit tests requiring different customizations will not be able to use the same custom attribute. In this case, developers must choose between two options:
Not using the
AutoData
-approachCreate a specialization of the attribute for each customization
A bit more advanced alternative is to create a customization method for each unit test and use reflection to invoke that method.
This gist contains a prototype of said approach. Below is presented a test fixture leveraging it.
Order of the parameters
When using the AutoData
and InlineAutoData
attributes, the order of parameters of the unit tests assume a critical importance, especially when using the Frozen
attribute.
For this reason, it's suggested to sort the parameters following this order
explicit parameters for the
InlineAutoData
attribute (if any)frozen parameters for the system under test,
the system under test
parameters to be used in the Act phase.
Here is an example
Compatibility issues
Unfortunately, the AutoData
and InlineAutoData
attributes are not compatible with the attributes built into NUnit shown earlier.
Last updated