Search code examples
unit-testing.net-coremoqxunitautofixture

.NET core write better unit tests with Xunit + Autofixture + Moq


In .NET Core for unit testing, I'm using Xunit, Moq, and Autofixture. But even with them, I see that my unit tests become complicated and take time.

Maybe someone could tell me if there are any ways to make this test smaller?

[Fact]
public async Task Verify_NotAuthorised_NoServiceSendInvoked()
{
    // Arrange
    var fixture = new Fixture()
        .Customize(new AutoMoqCustomization());

    var sut = fixture.Create<VerificationService>();

    var mockApiBuilder = fixture.Freeze<Mock<IApiEntityBuilder>>();
    //init mocked mockSendServiceOne, so later I could check if it was invoked or not
    var mockSendServiceOne = fixture.Freeze<Mock<ISendServiceOne>>();

    mockApiBuilder.Setup(x => x.Verification(It.IsAny<string>(), It.IsAny<string>()))
        .Returns(fixture.Create<VerificationEntity>());

    var call = fixture.Freeze<Mock<ISendServiceTwo>>();
    call.Setup(x => x.IsSuccessful()).Returns(false);

    // Act
    await sut.Verify(fixture.Create<string>(), fixture.Create<string>());

    // Assert
    mockSendServiceOne.Verify(x => x.Call(It.IsAny<SendServiceOneEntity>()), Times.Never);
}

The testing method itself

public async Task<CreatedEntity> Verify(string dataOne, string dataTwo)
{
   await _someCaller.Call(_apiEntityBuilder.Verification(dataOne, dataTwo));
   _someCaller.CreatePayment();

   if (!_someCaller.IsSuccessful()) return _someCaller.CreatedEntity;

   await mockSendServiceOne.Call(_apiEntityBuilder.Call(_someCaller.CreatedEntity.SpecificData));

   return _someCaller.CreatedEntity;
}

Here I am testing if isSuccessful() returns fasle then no mockSendServiceOne.Call should be invoked.

Could someone give me some feedback on how to write a better unit tests. Because only for this small check of code I had to write a lot of code to test it.


Solution

  • You can use AutoData Theories. (Links to a great post by Mark Seeman about this exact situation).

    In short AutoFixture has a built in attribute called AutoData which you can inherit from and then customize the fixture with the AutoMoqCustomization.

    You decorate your testmethod ([Theory]) with this attribute, and now autofixture automagically will generate any parameter you specify for your testmethod.

    When you would use the Freeze() method to generate an item, you put the [Frozen]attribute in front of the parameter.

    Here is an example of how to do it:

    public class TheTests
    {
        [Theory]
        [AutoDomainData]
        public void Verify_WhatWeWannaTest_CallsTheMethodOnTheDependency([Frozen] Mock<ITheDependency> dependency, WhatWeWannaTest sut)
        {
            // Act
            sut.CallTheDependency();
    
            // Assert
            dependency.Verify(x => x.TheMethod());
        }
    }
    
    // Create a AutoData attribute customized with Moq
    public class AutoDomainDataAttribute : AutoDataAttribute
    {
        public static IFixture FixtureFactory()
        {
            var f = new Fixture();
            // I like members of interfaces to be configured, so i set it her
            f.Customize(new AutoMoqCustomization { ConfigureMembers = true });
            return f;
        }
    
        public AutoDomainDataAttribute() : base(FixtureFactory) { }
    }
    
    // Simple class we can test
    public class WhatWeWannaTest
    {
        private readonly ITheDependency _theDependency;
    
        public WhatWeWannaTest(ITheDependency theDependency) { _theDependency = theDependency; }
    
        public void CallTheDependency()
        {
            _theDependency.TheMethod();
        }
    }
    
    // Simple dependency for WhatWeWannaTest
    public interface ITheDependency
    {
        int TheMethod();
    }