【问题标题】:Unit test passes when in debug but fails randomly when run [xUnit + Automoq + Autofixture]单元测试在调试时通过,但在运行时随机失败 [xUnit + Automoq + Autofixture]
【发布时间】:2022-12-14 03:47:57
【问题描述】:

在运行多个测试用例时,Moq 设置无法按预期使用 Automoq + Autofixture。

我创建了多个与我的方法相对应的测试用例。在我的测试运行中,随机测试失败发生并说明原因 - System.InvalidOperationException : The test method expected 3 parameter values, but 1 parameter value was provided.

代码设置 -

  • 属性类
public class AutoMoqDataAttribute : AutoDataAttribute
{
    public AutoMoqDataAttribute() : base(() =>
    {
        var fixture = new Fixture().Customize(new CompositeCustomization(
            new AutoMoqCustomization(),
            new SupportMutableValueTypesCustomization()));

        fixture.Behaviors.OfType<ThrowingRecursionBehavior>().ToList().ForEach(b => fixture.Behaviors.Remove(b));
        fixture.Behaviors.Add(new OmitOnRecursionBehavior());

        return fixture;
    })
    {
    }
}
  • 理论数据类
    public struct ExpectedValueTestData<TExpected>
    {
        public string Name;
        public Parameters Params;
        public TExpected ExpectedValue;

        public override string ToString()
        {
            return $"{this.Name}";
        }
    }

    public struct Parameters
    {
        public Campaign.Entities.Enum.RunningStatus currentStatus;
        public Campaign.Entities.Enum.RunningStatus updatedStatus;
        public Campaign.Entities.Enum.Application application;
    }

#region Test data
    public class ValidValueTests : TheoryData<ExpectedValueTestData<bool>>
    {
        public ValidValueTests()
        {
            this.Add(new ExpectedValueTestData<bool>
            {
                Name = @"SetRunningStatusActiveTest - valid call for xyz",
                Params = new Parameters
                {
                    currentStatus = RunningStatus.Daily_Goal_Reached,
                    updatedStatus = RunningStatus.Running,
                    application = Application.XYZ,
                },
                ExpectedValue = true,
            });
        }
    }
  • 测试用例类

public class SetRunningStatusActiveTestCase
{
    #region Theories
    [Theory, AutoMoqData]
    [ClassData(typeof(ValidValueTests))]
    public async Task SetRunningStatusActiveTest_WhenValidCampaignExist(ExpectedValueTestData<bool> data, [Frozen] Mock<ICampaignRunningStatusRepo> campaignRunningStatusRepoMock, CampaignRunningJob sut)
    {
        campaignRunningStatusRepoMock.Setup(x => x.GetCampaignsOnRunningStatus(It.IsAny<RunningStatus>(), It.IsAny<Application>())).ReturnsAsync(() => new List<int> { 1, 2, 3 }).Verifiable();
        campaignRunningStatusRepoMock.Setup(x => x.ConvertRunningStatus(It.IsAny<RunningStatus>(), It.IsAny<RunningStatus>(), It.IsAny<Application>())).ReturnsAsync(() => 3);

        var actual = await sut.SetRunningStatusActive(data.Params.currentStatus, data.Params.updatedStatus, data.Params.application);
        Assert.Equal(data.ExpectedValue, actual);
        campaignRunningStatusRepoMock.Verify();
    }

    [Theory, AutoMoqData]
    [ClassData(typeof(ZeroCampaignsForUpdateTests))]
    public async Task SetRunningStatusActiveTest_WhenNoCampaignExist(ExpectedValueTestData<bool> data, [Frozen] Mock<ICampaignRunningStatusRepo> mockCampaignRunningStatusRepo1, CampaignRunningJob sut)
    {
        mockCampaignRunningStatusRepo1.Setup(x => x.GetCampaignsOnRunningStatus(It.IsAny<RunningStatus>(), It.IsAny<Application>())).ReturnsAsync(() => new List<int> { });

        var actual = await sut.SetRunningStatusActive(data.Params.currentStatus, data.Params.updatedStatus, data.Params.application);
        Assert.Equal(data.ExpectedValue, actual);
    }

    [Theory, AutoMoqData]
    [ClassData(typeof(TestsThrowingException))]
    public async Task SetRunningStatusActiveTest_WhenThrowingException(TestThrowingExceptionData data, [Frozen] Mock<ICampaignRunningStatusRepo> mockCampaignRunningStatusRepo2, CampaignRunningJob sut)
    {
        mockCampaignRunningStatusRepo2.Setup<Task<List<int>>>(x => x.GetCampaignsOnRunningStatus(It.IsAny<RunningStatus>(), It.IsAny<Application>())).ReturnsAsync(() => new List<int> { 1, 2 });
        mockCampaignRunningStatusRepo2.Setup(x => x.ConvertRunningStatus(It.IsAny<RunningStatus>(), It.IsAny<RunningStatus>(), It.IsAny<Application>())).ThrowsAsync(new Exception());
        await Assert.ThrowsAsync<Exception>(() => sut.SetRunningStatusActive(data.Params.currentStatus, data.Params.updatedStatus, data.Params.application));
    }
    #endregion   
}
  • 随机中断快照

帮助我了解我在这里做错了什么?

【问题讨论】:

  • 错误消息表明您的其中一个类 ValidValueTestsZeroCampaignsForUpdateTestsTestsThrowingException 存在问题。他们的定义是什么?
  • @ChristopherHamkins 更新了描述中的理论数据结构。
  • 数据提供者类看起来确实不错,问题一定出在注入的对象上。
  • 我的模拟设置在某处保持冻结状态,这就是它破坏其他测试用例的原因。我无法理解这背后的原因,根据 xUnit,每个测试用例都将使用更新的类实例运行。

标签: unit-testing xunit xunit.net autofixture automoq


【解决方案1】:

AutoMoqData 和 ClassData 将各自生成自己的测试用例,它们不能合并。

您收到错误 System.InvalidOperationException : The test method expected 3 parameter values, but 1 parameter value was provided.,因为您在 TheoryData 类中提供了一个参数。

计划在 AutoFixture v5 中支持类数据。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-10-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多