Welcome Guest! To enable all features please Login or Register.

Notification

Icon
Error

NCrunch creates instances of attributes on tests, tripping up my behavior
DanHil
#1 Posted : Saturday, June 29, 2013 9:33:41 AM(UTC)
Rank: Advanced Member

Groups: Registered
Joined: 11/12/2012(UTC)
Posts: 85

Thanks: 11 times
Was thanked: 3 time(s) in 3 post(s)
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?
Remco
#2 Posted : Saturday, June 29, 2013 11:16:23 PM(UTC)
Rank: NCrunch Developer

Groups: Administrators
Joined: 4/16/2011(UTC)
Posts: 6,974

Thanks: 929 times
Was thanked: 1256 time(s) in 1169 post(s)
Hi Daniel -

Attributes are automatically constructed by the CLR when they are referenced via runtime reflection. NCrunch needs to use runtime reflection in order to identify critical metadata associated with the test. There really isn't a way around this.

I would generally advise against placing complex and/or non-repeatable logic in attribute constructors. This logic can very easily be invoked accidentally and wreck havoc with tools. Where necessary, you may wish to place a static member test around the logic to ensure it isn't executed twice - for example:

private static bool executed = false;

public MyCustomAttribute()
{
if (executed)
return;

executed = true;

... Place constructor logic here ...

}
DanHil
#3 Posted : Sunday, June 30, 2013 4:47:43 PM(UTC)
Rank: Advanced Member

Groups: Registered
Joined: 11/12/2012(UTC)
Posts: 85

Thanks: 11 times
Was thanked: 3 time(s) in 3 post(s)
That wouldn't work. The result would be that my customization would be executed once for the whole test suite. Not once per test.

My current solution is a hack: Don't execute the code if the attribute has been created by NCrunch: http://blog.fire-develop...crunch-and-global-state/
Remco
#4 Posted : Sunday, June 30, 2013 10:23:49 PM(UTC)
Rank: NCrunch Developer

Groups: Administrators
Joined: 4/16/2011(UTC)
Posts: 6,974

Thanks: 929 times
Was thanked: 1256 time(s) in 1169 post(s)
Yes, sorry, I misunderstood the goal. Using the call stack to infer context would seem like the only way to solve this problem. Thanks for writing up the blog post - I'm sure others will also find this very helpful.
Users browsing this topic
Guest
Forum Jump  
You cannot post new topics in this forum.
You cannot reply to topics in this forum.
You cannot delete your posts in this forum.
You cannot edit your posts in this forum.
You cannot create polls in this forum.
You cannot vote in polls in this forum.

YAF | YAF © 2003-2011, Yet Another Forum.NET
This page was generated in 0.039 seconds.
Trial NCrunch
Take NCrunch for a spin
Do your fingers a favour and supercharge your testing workflow
Free Download