.NET core 使用 Xunit + Autofixture + Moq 编写更好的单元测试

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

在用于单元测试的 .NET Core 中,我使用 Xunit、Moq 和 Autofixture。但即使有了它们,我发现我的单元测试变得复杂并且需要时间。

也许有人可以告诉我是否有任何方法可以使这个测试更小?

[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);
}

测试方法本身

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;
}

这里我正在测试是否 isSuccessful() returns fasle 然后不应该调用 mockSendServiceOne.Call。

有人可以给我一些关于如何编写更好的单元测试的反馈吗? 因为只是为了这个小的代码检查,我不得不写很多代码来测试它。

您可以使用 AutoData Theories。 (链接到 Mark Seeman 关于这种确切情况的精彩 post)。

简而言之,AutoFixture 有一个名为 AutoData 的内置属性,您可以继承该属性,然后使用 AutoMoqCustomization 自定义夹具。

你用这个属性装饰你的测试方法([Theory]),现在 autofixture 会自动生成你为你的测试方法指定的任何参数。

当您使用 Freeze() 方法生成项目时,您将 [Frozen] 属性放在参数前面。

这是一个如何操作的例子:

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();
}