cSharpTesting.png

The Holy Trinity of C# Unit Testing

Getting started unit testing is hard. Really hard.

To phrase it like the late Mitch Hedberg, “Man, you don’t know how hard it is to quit smoking.  Yes I do, it’s as hard as it is to start unit testing!“.  He didn’t actually say that, but he would have if he was a programmer.

What makes it so hard to start unit testing is the damn legacy code.  People say the problem with testing legacy stuff is all the strongly coupled code.  I say, we wouldn’t have to worry about all the dependencies if it wasn’t for all the freaking data.  Seriously, how are you supposed to write quick executing tests with all these damn database calls directly in your services.

So you get a nice dependency injection framework.  You refactor all of the queries out into repositories nicely injected into your services. Then you start down the long road of writing tests.  You’ve still got a problem here.

How do I get the test data to run through your services?  Get a mocking framework.

Now you’ve got another problem, getting test data into your mock repos takes 10 times more coding than the stupid test itself.

Fear not!  Let me introduce you to the testing trinity that actually made this fun for me.

  1. Moq
  2. xUnit
  3. AutoFixture

Note: Make sure you also get the AutoFixture.AutoMoq and AutoFixture.Xunit nuget packages

Moq

Moq is a good all around mocking framework.  The basic setup goes as follows:

[Fact]
public void Basic_Unit_Test()
{
    var mockRepo = new Mock<IMyRepo>();
    mockRepo.Setup(foo => foo.GetSomething(It.IsAny<int>())).Returns("Something");
    var serviceUnderTest = new MyUpperService(mockRepo);
    var result = serviceUnderTest.MakeUpper();
    Assert.Equal("SOMETHING",result);
}

The idea here is pretty simple. We’ve got a repository that just returns a string. We’ve made a mock of the repo that just returns “Something” for any integer passed in. We supply this mock repo to our service under test through the constructor as it would be when generated by an IoC container. We then call a method that makes use of the mock repository and assert that the service logic gives us a result that we expect. Moq has plenty of other options beyond this example. For instance you can supply different output for different parameter values or even throw an exception if you’re trying to test how your service handles errors. For now I’ll move on as I am more interested in how well this plays nice with AutoFixture and xUnit.

xUnit

xUnit is a successor to NUnit. It has pared down the feature list keeping the so called good stuff. For the most part it works about the same, just removing the need for the [TestFixture] attribute on the top of the test class and renaming the [Test] method attribute with [Fact]. The real beauty of xUnit though is the [Theory] type test. Theories are data centric tests that enable the test method to take in a parameter so you can run your test with several different data seeds. Take a look at this example:

[Theory]
[InlineData(2)]
[InlineData(0)]
[InlineData(-1)]
public void Should_Correctly_Detect_Negative(int value)
{
    var service = new NegativeService();
    if(value < 0)
        Assert.True(service.IsNegative(value));
    else
        Assert.False(service.IsNegative(value));
}

This is kinda cool, we can run the same test on multiple values.  Another usage of this would be to make sure the service is durable under several different parameter configurations without having to rewrite the same test.  This is all fine and dandy but lets bring AutoFixture into the mix so we can get to the good stuff.

AutoFixture

AutoFixture aims to remove test data setup by auto generating values to fill variables or class fields.  At its base its pretty simple, see this example:

var fixture = new Fixture();

var someString = fixture.Create<string>();
Console.WriteLine(someString);
// 30a35da1-d681-441b-9db3-77ff51728b58

var generatedPerson = fixture.Create<Person>();
Console.WriteLine(generatedPerson.Name);
// namef5cdf6b1-a473-410f-95f3-f427f7abb0c7

Console.WriteLine(generatedPerson.Age);
// 1

This is great! Have a complex object with over a hundred fields because the marketing department won’t stop bothering your customers about their personal life? No Problem! AutoFixture will slap junk data in there so you wont have to worry about wiring in the mock data by hand. Its really nifty.

Now lets take a look at putting all three of these bad boys together.

[Theory, AutoMoqData]
public void Should_Throw_On_Invalid_Email([Frozen] Mock<IProductRepo> productRepo,
    Product product, Customer customer, TransactionService testTransactionService)
{
    customer.Email = "Not an email address";
    productRepo.setup(repo => repo.GetProduct(It.Is<int>(product.id)).Returns(product);
    Assert.Throws(() => testTransactionService.Sell(product.Id,customer));
}

What the what? Did that just happen?! Looks like we’re injecting similar to how we would with a dependency injection framework. By declaring productRepo as [Frozen] AutoFixture injects our mock ProductRepo into TransactionService so all we have to do is setup the GetProduct method with the proper output. Product and Customer will be automatically filled with junk data. Here we want to make sure that our TransactionService will throw an exception if customer has an invalid email address. We just set the customer email to a value that will produce the expected result and pass it through to the Sell method. Really cool right?!

One last thing to mention, you’ll notice that along with the Theory attribute we’re also decorating the test method with the AutoMoqData attribute. This didn’t come out of the box with the nuget packages so I’ll give you the class I found while researching this on the internet.

using Ploeh.AutoFixture;
using Ploeh.AutoFixture.AutoMoq;
using Ploeh.AutoFixture.Xunit;

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

Well, there you go! That’s my testing setup in a nutshell. There’s more to expand on what you can do with both Moq and AutoFixture. Both of their Github readme files are pretty robust, I suggest you read through them. Good luck in your own testing adventures!

Leave a comment below!

comments

Stacy GayThe Holy Trinity of C# Unit Testing
Share this post