Search code examples
c#asp.net-corexunitautofixture

Xunit inline auto data with autofixture question


i have created an custom inline auto moq attribute as below :

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 class InlineAutoMoqData : InlineAutoDataAttribute
{
    public InlineAutoMoqData(params object[] objects) : base(new AutoMoqDataAttribute(), objects)
    {

    } 
}

I love the autofixture as it auto populate data for me if I didn't provide the test data via the attribute. So far I am able to use inline data to pass in null, string, int and etc but how do I pass in a empty list or a object? For example on below test method. Please feel free to modify my code to make it work as I am quite new to unit test.

    [Theory]
    [InlineAutoMoqData(null)]
    public void UpdatePortfolioFiles_InvalidInput_ShouldThrowException(List<MyClass> mytestList, MyTestService sut)
    {
       
        //do something here
       
    }enter code here

Solution

  • The scenario you describe sounds like a good use case for the MemberAutoDataAttribute. This attribute is similar to the MemberDataAttribute from xUnit, and it should allow you to provide parameters to a test method from a static member (field/property/method).

    There is currently a bug in the MemberAutoDataAttribute where it provides only the first set of parameters from the member. There is a workaround published by a community member here.

    I will try to get a fix out for the attribute soon.

    public static IEnumerable<object[]> TestCases()
    { 
        yield return new object[] { new List<MyClass>() };
        yield return new object[] { new HashSet<MyClass>() };
        yield return new object[] { new LinkedList<MyClass>() };
    }
    
    [Theory]
    [MemberDomainData(nameof(TestCases))]
    public void UpdatePortfolioFiles_InvalidInput_ShouldThrowException(
        IEnumerable<MyClass> mytestList, MyClass sut)
    {
        Assert.Empty(mytestList);
        Assert.NotNull(sut);
    }
    
    public class MemberDomainData : MemberAutoDataAttribute
    {
        public MemberDomainData(string memberName, params object[] parameters)
            : base(new DomainDataAttribute(), memberName, parameters)
        {
        }
    }
    

    If you do not want to use the workaround implementation, you could use the declarative syntax to create the empty collections, until the fix comes out.

    Let me know if this solves your issue.

    As an advice you could encapsulate the recursion omitting behavior in a separate customization. Customizations are meant to be small reusable and composable.

    public class OmitRecursionCustomization : ICustomization
    {
        public void Customize(IFixture fixture)
        {
            fixture.Behaviors.OfType<ThrowingRecursionBehavior>().ToList().ForEach(b =>
            fixture.Behaviors.Remove(b));
            fixture.Behaviors.Add(new OmitOnRecursionBehavior());
        }
    }
    
    public class DomainCustomization : CompositeCustomization
    {
        public DomainCustomization()
            : base(
                new AutoMoqCustomization(),
                new SupportMutableValueTypesCustomization(),
                new OmitRecursionCustomization())
        {
        }
    }
    
    public class DomainDataAttribute : AutoDataAttribute
    {
        public DomainDataAttribute()
            : base(() => new Fixture().Customize(new DomainCustomization()))
        {
        }
    }