Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XUnit, AutoFixture and Moq best practice

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.

like image 265
Matteo Mosca Avatar asked Dec 18 '14 09:12

Matteo Mosca


People also ask

What is AutoFixture xUnit?

AutoFixture is a library that you can use alongside your testing framework to reduce the amount of boilerplate test code you need to write and thus improve your productivity. At its core, AutoFixture helps you setup your tests by generating anonymous test data for you.

What is MOQ and xUnit?

Moq and xUnit belong to "Testing Frameworks" category of the tech stack. xUnit is an open source tool with 2.62K GitHub stars and 610 GitHub forks. Here's a link to xUnit's open source repository on GitHub.

Does xUnit have setup?

xUnit.net offers several methods for sharing this setup and cleanup code, depending on the scope of things to be shared, as well as the expense associated with the setup and cleanup code.

What is auto fixture?

AutoFixture is an open source library for . NET designed to minimize the 'Arrange' phase of your unit tests in order to maximize maintainability.


1 Answers

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);
    }
}
like image 186
Nikos Baxevanis Avatar answered Sep 22 '22 09:09

Nikos Baxevanis