XUnit, AutoFixture and Moq best practice

Matteo Mosca picture Matteo Mosca · Dec 18, 2014 · Viewed 14.5k times · Source

I'm reading a lot of documentation and examples about how to properly unit test things combining the three components in the title. I came up with a test method for a method on my business logic, but it feels very clunky and dirty.

I'd like to get some feedback from people more experienced on this topic to see how I can improve it.

Here's the code, explanation follows:

[Fact]
public void ShouldGetItemWithSameId()
{
    var fixture = new Fixture().Customize(new AutoMoqCustomization());
    var facade = fixture.Freeze<Mock<IDataFacade>>();
    facade.Setup(c => c.Get(It.IsAny<int>())).Returns((int i) => new Item { Key = i });

    var sut = fixture.Create<BusinessLogic>();
    var expected = fixture.Create<int>();

    Assert.Equal(expected, sut.Get(expected).Key);
}

My BusinessLogic class takes an IDataFacade as constructor parameter, which is responsible in its Get(int) method to retrieve the item with the same Id, pretty basic stuff.

I freeze the IDataFacade mock and I set it up to construct an object matching the Id in It.IsAny<int>. I then create my SUT and test it. Works fine.

I'd like to understand if I can improve things considering the following:

  • I have to test more complex methods, like a Query method that takes a class containing a lot of properties that will be used as filters on matching properties on the type being queried. In this case I wouldn't know how to properly do the "Setup" part of the mock, since I have to initialize all, or close to all, the properties of the returned type, and in this scenario it's not a single Item but a whole collection
  • The setup part feels out of place, I'd like to be able to reuse it in more methods

I have some other tests using Theory with AutoMoqData but I was unable to achieve this test (and I think the more complex ones) using that approach, so I switched back to plain Fact with manually instantiated fixture.

Any help will be extremely appreciated.

Answer

Nikos Baxevanis picture Nikos Baxevanis · Dec 18, 2014

Overall, the original test looks good. It's not possible nor easy to extract the setup of Stubs and Mocks out of the test, in a generic fashion.

What you can do though, is minimize the Arrange phase of the test. Here's the original test re-written using AutoFixture.Xunit's own unit-testing DSL:

[Theory, TestConventions]
public void ShouldGetItemWithSameId(
    [Frozen]Mock<IDataFacade> facadeStub,
    BusinessLogic sut,
    int expected)
{
    facadeStub
        .Setup(c => c.Get(It.IsAny<int>()))
        .Returns((int i) => new Item { Key = i });

    var result = sut.Get(expected);
    var actual = result.Key;

    Assert.Equal(expected, actual);
}

The TestConventions attribute is defined as:

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

HTH


Sample types used in the example:

public class Item
{
    public int Key { get; set; }
}

public interface IDataFacade
{
    Item Get(int p);
}

public class BusinessLogic
{
    private readonly IDataFacade facade;

    public BusinessLogic(IDataFacade facade)
    {
        this.facade = facade;
    }

    public Item Get(int p)
    {
        return this.facade.Get(p);
    }
}