unit-testingxunitxunit.netautofixtureautomoq

Unit test passes when in debug but fails randomly when run [xUnit + Automoq + Autofixture]


Moq setup doesn't work as expected using Automoq + Autofixture while running multiple test cases.

I created multiple test cases corresponding to my method. In my test run, random test failures occur with stating reason - System.InvalidOperationException : The test method expected 3 parameter values, but 1 parameter value was provided.

Code Setup -

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   
}

enter image description here

enter image description here

Help me to get the insights what I am doing wrong here?


Solution

  • Solved my problem using below steps -

    Created ClassAutoMoqDataAttribute on top of AutoMoq that can adopt signature pattern.

    public class ClassAutoMoqDataAttribute : CompositeDataAttribute
        {
            public ClassAutoMoqDataAttribute(Type values)
                : base(new ClassDataAttribute(values), new AutoMoqDataAttribute())
            {
            }
        }
    

    Then Updated Test Case accordingly

    public class SetRunningStatusActiveTestCase
    {
        #region Theories
    [Theory]
    [ClassAutoMoqData(typeof(ValidValueTests))]
            public async Task ConvertRunningStatusTest_WhenValidCampaignExist_ReturnsTrue(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();
            }
        }