When writing unit tests, I use AutoFixture with its xUnit extensions, specifically with the support for
automatically created data.
AutoFixture is a framework for creating "anonymous objects". For example, when you want to test a method that takes a string, it generates one for you.
It has an extension for xUnit's Theory that allows automatic creation of these anonymous objects based on the parameters of the test method.
Details on how this works are in the Blog post, but in summary, a test looks like this:
Code:
[Theory, AutoTestData]
public void CallingFoo_ReturnsParameterUnchanged(string parameter, Foo sut)
{
var actual = sut.Foo(parameter);
Assert.Equal(parameter, actual);
}
AutoFixture allows extensive customizations to be performed. For example, it can be configured to use a certain factory when creating an instance of Foo.
This customization usually is specific per test project and is implemented in its own class.
To make this customization available to AutoFixture, the attribute AutoTestData is also implemented once per test project and it looks like so:
Code:
public class AutoTestData : AutoData /* base class from AutoFixture */
{
public AutoTestData() : base(new Fixture().Customize(new MyCustomizations())) { }
}
The important point here is this line:
Code:
new Fixture().Customize(new MyCustomizations())
This
executes the customization.
Now, xUnit creates an instance of that attribute and NCrunch creates one as well, leading to my customizations being run twice.
That usually isn't a problem, but now I am trying to test ViewModels created with MvvmCross and it makes heavy use of Singletons.
In my customization, I am initializing these Singletons with mocks automatically created by AutoFixture and frozen in AutoFixture, i.e. every request for that interface will return the same instance.
Now, to the actual problem. What happens is the following:
1. xUnit creates an instance of my attribute, triggering the customization. In this customization, I get the instance of the interface, register it as a "local" singleton with the AutoFixture instance and as a global one with MvvmCross. This instance is the one that xUnit / AutoFixture are supplying to my unit test
2. NCrunch creates an instance of my attribute, again triggering the customization. The instance created now is now supplied to my test, however, it overrides the global singleton registered with MvvmCross.
That leads to the scenario that the Code Under Test that uses the MvvmCross Singleton operates on one instance while my test gets passed another instance.
Is there really a need to instantiate all attributes, even the one NCrunch doesn't know how to handle?