AutoFixture 2.0 comes with a new extension for xUnit.net data theories. For those of us using xUnit.net, it can help make our unit tests more succinct and declarative.

AutoFixture's support for xUnit.net is implemented in a separate assembly. AutoFixture itself has no dependency on xUnit.net, and if you use another unit testing framework, you can just ignore the existence of the Ploeh.AutoFixture.Xunit assembly.

Let's go back and revisit the previous test we wrote using AutoFixture and its auto-mocking extension:

[Fact]
public void AddWillPipeMapCorrectly()
{
    // Fixture setup
    var fixture = new Fixture()
        .Customize(new AutoMoqCustomization());
 
    var basket = fixture.Freeze<Basket>();
    var mapMock = fixture.Freeze<Mock<IPizzaMap>>();
 
    var pizza = fixture.CreateAnonymous<PizzaPresenter>();
 
    var sut = fixture.CreateAnonymous<BasketPresenter>();
    // Exercise system
    sut.Add(pizza);
    // Verify outcome
    mapMock.Verify(m => m.Pipe(pizza, basket.Add));
    // Teardown
}

Notice how all of the Fixture Setup phase is only used to create various objects that will be used in the test. First we create the fixture object, and then we use it to create four other objects. That turns out to be a pretty common idiom when using AutoFixture, so it's worthwhile to reduce the clutter if possible.

With xUnit.net's excellent extensibility features, we can. AutoFixture 2.0 now includes the AutoDataAttribute in a separate assembly. AutoDataAttribute derives from xUnit.net's DataAttribute (just like InlineDataAttribute), and while we can use it as is, it becomes really powerful if we combine it with auto-mocking like this:

public class AutoMoqDataAttribute : AutoDataAttribute
{
    public AutoMoqDataAttribute()
        : base(new Fixture()
            .Customize(new AutoMoqCustomization()))
    {
    }
}

This is a custom attribute that combines AutoFixture's two optional extensions for auto-mocking and xUnit.net support. With the AutoMoqDataAttribute in place, we can now rewrite the above test like this:

[Theory, AutoMoqData]
public void AddWillPipeMapCorrectly([Frozen]Basket basket,
    [Frozen]Mock<IPizzaMap> mapMock, PizzaPresenter pizza,
    BasketPresenter sut)
{
    // Fixture setup
    // Exercise system
    sut.Add(pizza);
    // Verify outcome
    mapMock.Verify(m => m.Pipe(pizza, basket.Add));
    // Teardown
}

The AutoDataAttribute simply uses a Fixture object to create the objects declared in the unit tests parameter list. Notice that the test is no longer a [Fact], but rather a [Theory].

The only slightly tricky thing to notice is that when we declare a parameter object, it will automatically map to a call to the CreateAnonymous method for the parameter type. However, when we need to invoke the Freeze method, we can add the [Frozen] attribute in front of the parameter.

The best part about data theories is that they don't prevent us from writing normal unit tests in the same Test Class, and this carries over to the [AutoData] attribute. We can use it when it's possible, but for those more complex tests where we need to interact with a Fixture instance, we can still write a normal [Fact].


Comments

I'm poking around at the AutoDataAttribute and I might be missing something but in a simple test I'm getting an InvalidOperationException:

System.InvalidOperationException : No data found for XunitSample.TestClass.TestMethod2
System.InvalidOperationException : No data found for XunitSample.TestClass.TestMethod1

Did I miss something in terms of setup? Sample code here - https://gist.github.com/1920943
2012-02-27 02:49 UTC
I just copied in the code from your gist and added the appropriate NuGet packages, followed by an Add-BindingRedirect, and everything works as intended. In other words, I can't reproduce the problem based on the information given here. I'd love to help, but I don't think this is the correct forum. Could you ask on Stack Overflow instead? If so, I'll take a look.
2012-02-27 06:57 UTC
Brian McCord #
I am very impressed with this way of doing things, but I have one question. If you need to do setup on a mock to force certain return values, are you forced to go back to using a Fact and setting up the mock manually?
2012-03-02 17:00 UTC
Absolutely, that's what the [Frozen] attribute is for. Here's an example (second test).
2012-03-03 19:42 UTC
Jeff Soper #

Forgive me if I missed this point in your blog, but it seems that the parameter order is critical when injecting objects marked with the [Frozen] attribute into your test, if the SUT is being injected with that frozen object.

I was refactoring my tests to make use of AutoData like you've shown here, and was adding my [Frozen]Mock<IMySutDependency> parameter willy-nilly to the end of the test parameters, *after* the SUT parameter. I was freezing it so that I could verify the same mocked dependency that was injected into the SUT as part of my test, or so I thought...

void MySutTest(MySut sut, [Frozen]Mock<IMySutDependency> frozen)

After a brief episode of confusion and self-loathing, I moved the frozen mocked dependency in front of the SUT in the test parameter list. Skies parted, angels sang, test passed again.

void MySutTest([Frozen]Mock<IMySutDependency> frozen, MySut sut)

2013-08-22 23:00 UTC

Jeff, thank you for your comment. As you have discovered, the order of test parameters matter when you apply those 'hint' attributes, like [Frozen]. This is by design, because it enables you to generate one or more unique values of a data type before you freeze it. This could sometimes be important, although it's not something I do a lot. Keep in mind that the [Frozen] attribute wasn't designed exclusively with mocks in mind; it doesn't know anything about mocks - it just freeze a Type.

In general, all of those 'hint' attributes apply an ICustomization, and they apply each ICustomization in the order of the arguments to which they are applied. The order of AutoFixture Customizations matter.

2013-08-24 12:39 UTC


Wish to comment?

You can add a comment to this post by sending me a pull request. Alternatively, you can discuss this post on Twitter or somewhere else with a permalink. Ping me with the link, and I may respond.

Published

Friday, 08 October 2010 08:48:07 UTC

Tags



"Our team wholeheartedly endorses Mark. His expert service provides tremendous value."
Hire me!
Published: Friday, 08 October 2010 08:48:07 UTC