Simplify test cases in .NET with Autofixture and Moq

Readability of unit tests

Like I've mentioned before, in other articles about unit testing, I'm a strong believer in tests as specifications for your classes. Recently, I've heard it being talked of as 'insurance' and I find myself nodding in quiet agreement. I'd rather have tests than not.

Every so often, though, depending on the complexity of your system under test (SUT), your tests become unwieldy and rather unreadable. Here's an example:

 1[Fact]
 2public voidExtractDbMetadata_WhenInputIsValid_ThenConnectonCreatesCommand()
 3{
 4  //Arrange
 5  var fixture = new Fixture();
 6  var conn = new Mock<IDbConnection>();
 7  var cmd = new FakeDbCommand();
 8  var compiler = new FakeCompiler();
 9  var sv = new Mock<ISqlVendorService>();
10  var cdc = fixture.Create<CodeGenConfig>();
11  var sut = new SchemataExtractorService(sv.Object);
12  
13  sv.Setup(vendorSvc => vendorSvc.GetDbInterfaceProviders(cdc)).Returns((conn.Object, compiler));
14  conn.Setup(c => c.CreateCommand()).Returns(cmd);
15  conn.SetupGet(c => c.State).Returns(ConnectionState.Open);
16  
17  //Act
18  var columns = sut.ExtractDbMetadata(cdc).GetAwaiter().GetResult();
19  
20  //Assert
21  conn.Verify();
22  columns.Should().BeEmpty();
23}
24... 
25
26internal class FakeDbCommand : DbCommand
27{
28  ...
29  protected override DbParameterCollection DbParameterCollection { get; } = Mock.Of<DbParameterCollection>();
30  protected override DbParameter CreateDbParameter() => Mock.Of<DbParameter>();
31  protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior) => Mock.Of<DbDataReader>();
32}

By the way, I've trimmed that implementation of DbCommand in the code excerpt above. The actual implementation goes on for much longer.

This distracts the maintainer of that piece of code. You really have to sift through a lot of setup code in order to understand the intent here. Would you rather not have this? I know I would. So let's look at an alternative.

 1[Theory, AutoTestParams]
 2internal void ExtractDbMetadata_WhenValid_ThenConnectionCreatesCommand(
 3    [Frozen] Mock<IDbConnection> conn, [Frozen] DbCommand cmd,
 4    [Frozen] Compiler compiler, [Frozen] Mock<ISqlVendorService> sv,
 5    CodeGenConfig cdc, SchemataExtractorService sut)
 6{
 7    //Arrange
 8    sv.Setup(vendorSvc => vendorSvc.GetDbInterfaceProviders(cdc)).Returns((conn.Object, compiler));
 9    conn.Setup(c => c.CreateCommand()).Returns(cmd);
10    conn.SetupGet(c => c.State).Returns(ConnectionState.Open);
11    
12    //Act
13    var columns = sut.ExtractDbMetadata(cdc).GetAwaiter().GetResult();
14    
15    //Assert
16    conn.Verify();
17    columns.Should().BeEmpty();
18}

Wowzer! That's looks much cleaner, doesn't it? Well, that's because... it is! No additional fake objects to work around mocking limitations. Test method focuses on critical setup and the actual test itself, rather than huge amounts of plumbing code. The code is more maintainable. If the code evolved to add another dependency, that would result in more plumbing in the former approach. In the latter approach, it would only be needed if it is critical to this test in any way. This makes the tests more maintainable and less brittle. If you find yourself nodding vigorously, then read on. Let me show you how you could make the setup simpler and easier to read using Autofixture and Moq.

Autofixture and Moq

Using a coherent test stack enhances each constituent greatly, such that the end result is greater than the sum of its parts. Such is the case with Autofixture and Moq. Using the Autofixture.AutoMoq library, we can easily turn Autofixture into an auto-mocking container, which allows us to write succinct, simpler tests. I will first show you how to do it manually, and then speak about how this can be made even easier with Auzaar.Autofixture.

The first thing you need is a reference to Autofixture.AutoMoq.

You then need to define an AutoTestParamsAttribute in your test library, like so:

 1[AttributeUsage(AttributeTargets.Method)]
 2public class AutoTestParamsAttribute : AutoDataAttribute
 3{
 4    public AutoTestParamsAttribute() 
 5        : base(() =>
 6        {
 7            var fixture = new Fixture()
 8                .Customize(new AutoMoqCustomization());
 9            return fixture;
10        })
11    {
12    }
13}

And that's pretty much it. Go on and add the AutoTestParams attribute to your theories. You can get Autofixture to freeze some of the input parameters (effectively treat them as singletons) as shown in the example above.

Eventually, you might find that your needs have grown. You might need an AutoTestParamsInlineData attribute or a AutoTestParamsMemberData attribute. Or you might need additional customizations or find yourself constructing specimen builders for your custom types. Auzaar.Autofixture includes all of this and more. Just reference it to ramp up your Autofixture capabilities.

That's a quick and easy way to simplify your unit tests when using Autofixture, Moq and XUnit.