AutoFixture customization for ASP.Net Core Web API controller

Lets start with simple controller:

[Route("api/[controller]")
public class TestController : Controller
{
	[HttpGet("")]
	public IActionResult Get()
	{
		return Ok();
	}
}

Now we want to create unit test for this method using AutoFixture/Moq/Shouldly/XUnit stack:

public class TestControllerTests 
{
	[Theory, AutoMoqData]
	public void Get_ShouldReturn_HttpStatusOk(TestController sut)
	{
		// act..
		var actual = sut.Get();

		// assert..
		actual.ShouldNotBeNull();
		actual.ShouldBeOfType()
	}
}

Test runner will crash saying:

AutoFixture.ObjectCreationException : AutoFixture was unable to create an instance from Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary because creation unexpectedly failed with exception. Please refer to the inner exception to investigate the root cause of the failure.

Request path:
	  J2BI.Holidays.TravelDocs.Api.Controllers.AccommodationController sut --> 
	   J2BI.Holidays.TravelDocs.Api.Controllers.AccommodationController --> 
	    Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary ViewData --> 
	     Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary


---- System.Reflection.TargetInvocationException : Exception has been thrown by the target of an invocation.
-------- Castle.DynamicProxy.InvalidProxyConstructorArgumentsException : Can not instantiate proxy of class: Microsoft.AspNetCore.Mvc.ModelBinding.ModelMetadata.
Could not find a parameterless constructor.

The problem lies in lack of parameterless constructor for ModelMetadata class.
This can easily be fixed with AutoFixture customization:

public class ApiControllerCustomization : ICustomization
{
    public void Customize(IFixture fixture)
    {
        fixture.Register(() => new CustomCompositeMetadataDetailsProvider());
        fixture.Inject(new ViewDataDictionary(fixture.Create(), fixture.Create()));
    }

    private class CustomCompositeMetadataDetailsProvider : ICompositeMetadataDetailsProvider
    {
        public void CreateBindingMetadata(BindingMetadataProviderContext context)
        {
            throw new System.NotImplementedException();
        }

        public void CreateDisplayMetadata(DisplayMetadataProviderContext context)
        {
            throw new System.NotImplementedException();
        }

        public void CreateValidationMetadata(ValidationMetadataProviderContext context)
        {
            throw new System.NotImplementedException();
        }
    }
}

This now has to be wrapped in data attribute:

public class AutoApiMoqDataAttribute : AutoDataAttribute
{
    public AutoApiMoqDataAttribute()
        : base(() =>
        {
            var fixture = new Fixture();

            fixture.Customize(new ApiControllerCustomization())
                   .Customize(new AutoMoqCustomization())
                   .Behaviors.Add(new OmitOnRecursionBehavior());

            return fixture;
        })
    {
    }
}

Voila! Now the test will pass.

Check out this gist

5 thoughts on “AutoFixture customization for ASP.Net Core Web API controller”

  1. AutoFixture was unable to create an instance from System.Reflection.TypeInfo because creation unexpectedly failed with exception. Please refer to the inner exception to investigate the root cause of the failure.

    Request path:
    Frontpoint.Payments.Api.Controllers.PaymentMethodsController sut
    Frontpoint.Payments.Api.Controllers.PaymentMethodsController
    Microsoft.AspNetCore.Mvc.ControllerContext ControllerContext
    Microsoft.AspNetCore.Mvc.ControllerContext
    Microsoft.AspNetCore.Mvc.Controllers.ControllerActionDescriptor ActionDescriptor
    Microsoft.AspNetCore.Mvc.Controllers.ControllerActionDescriptor
    System.Reflection.TypeInfo ControllerTypeInfo
    System.Reflection.TypeInfo

  2. You can try this for ControllerBase
    “`
    internal class ControllerBaseCustomization : ICustomization
    {
    public void Customize(IFixture fixture)
    {
    fixture.Customizations.Add(
    new FilteringSpecimenBuilder(
    new Postprocessor(
    new MethodInvoker(
    new ModestConstructorQuery()),
    new ControllerBaseFiller()),
    new ControllerBaseSpecification()));
    }

    private class ControllerBaseFiller : ISpecimenCommand
    {
    public void Execute(object specimen, ISpecimenContext context)
    {
    if (specimen == null) throw new ArgumentNullException(nameof(specimen));
    if (context == null) throw new ArgumentNullException(nameof(context));

    if (specimen is ControllerBase controller)
    {
    controller.ControllerContext = new ControllerContext
    {
    HttpContext = (HttpContext)context.Resolve(typeof(HttpContext))
    };
    }
    else
    {
    throw new ArgumentException(“The specimen must be an instance of ControllerBase”, nameof(specimen));
    }
    }
    }

    private class ControllerBaseSpecification : IRequestSpecification
    {
    public bool IsSatisfiedBy(object request) => request is Type type && typeof(ControllerBase).IsAssignableFrom(type);
    }
    }
    “`

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.