Search code examples
c#xunitautofixturexunit2

AutoFixture, xUnit: Setup TypeRelays and inject service in constructor


I am trying to write some tests, I use xUnit.net, Moq, AutoFixture. I need to inject service to my test method:

[Theory, AutoData]
public void TestSmthCool(IService service)
{
}

The IService has 3 dependencies which I want to mock. But, If I run the test I get error:

AutoFixture was unable to create an instance from Services.Interfaces.IService because it's an interface.

So, I fixed it in the following way:

[Theory, AutoData]
public void TestSmthCool()
{
   var fixture = new Fixture();
   fixture.Customize(new AutoMoqCustomization());
   fixture.Customizations.Add(
      new TypeRelay(
         typeof(IService),
         typeof(MyService)
      )
   );

   var s= fixture.Create<IService>();
}

But, how to setup TypeRelay for all tests and inject service via method constructor?


Solution

  • If you wish to use MyService for IService, then you don't need AutoMoqCustomization; i.e. this test passes:

    [Fact]
    public void TestSmthCool()
    {
        var fixture = new Fixture();
        fixture.Customizations.Add(
            new TypeRelay(
                typeof(IService),
                typeof(MyService)
            )
        );
    
        var s = fixture.Create<IService>();
    
        Assert.IsAssignableFrom<MyService>(s);
    }
    

    If you want to automate this, you can first package the TypeRelay in an ICustomization:

    public class MyServiceCustomization : ICustomization
    {
        public void Customize(IFixture fixture)
        {
            fixture.Customizations.Add(
               new TypeRelay(
                  typeof(IService),
                  typeof(MyService)));
        }
    }
    

    Then create an attribute that derives from AutoDataAttribute:

    public class MyServiceAutoDataAttribute : AutoDataAttribute
    {
        public MyServiceAutoDataAttribute() :
            base(new Fixture().Customize(new MyServiceCustomization()))
        {
        }
    }
    

    You can then use that in all your tests:

    [Theory, MyServiceAutoData]
    public void CustomizedAutoDataBasedTest(IService s)
    {
        Assert.IsAssignableFrom<MyService>(s);
    }
    

    In general, I tend to create a code base-wide CompositeCustomization that I apply indiscriminately to all tests.