Reputation: 91
We are attempting to create a simple unit test for our ASP.Net Core API controllers. We are using autofixture with autoMoq, XUnit2, and shoudly. How do we mock the TypeInfo creation? Or is there a better approach?
We followed this post to resolve the initial error of:
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.
namespace Tests.Controllers
{
using Api.Controllers;
using AutoFixture;
using AutoFixture.AutoMoq;
using Shouldly;
using System.Threading.Tasks;
using Xunit;
public class DiagnosticControllerTests
{
private readonly DiagnosticController _sut;
public DiagnosticControllerTests()
{
var fixture = new Fixture();
fixture.Customize(new AutoMoqCustomization())
.Customize(new ApiControllerCustomization()); // from Matt Uliasz see link above
_sut = fixture.Create<DiagnosticController>();
}
[Fact]
public async Task Ping_Returns_True()
{
var actual = await _sut.Ping();
actual.Data.ShouldBe(true);
}
}
}
This is throwing the following errors:
AutoFixture.ObjectCreationExceptionWithPath 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. ... Inner exception messages: Castle.DynamicProxy.InvalidProxyConstructorArgumentsException: Can not instantiate proxy of class: System.Reflection.TypeInfo. Could not find a parameterless constructor.
Edit After further testing, the error disappears when we stop deriving from the class Microsoft.AspNetCore.Mvc.Controller.
namespace Api.Controllers
{
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Models;
using System.Threading.Tasks;
[Authorize]
[Route("api/[controller]")]
public class DiagnosticController: Controller
{
[AllowAnonymous]
[HttpGet]
[Route("ping")]
public Task<PingResultDto> Ping()
{
var result = new PingResultDto
{
Data = true
};
return Task.FromResult(result);
}
}
}
Edit 2 Our current work-around is to not use AutoFixture/AutoMoq:
var sut = new Mock<DiagnosticController>().Object; //This works
Upvotes: 1
Views: 1881
Reputation: 1173
Try this:
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);
}
}
Upvotes: 2
Reputation: 11
Note that my answer below doesn't really address your concern but this is more of a tip:
I think you are overcomplicating your test.
Remember that Unit Testing are reserved for Core business logic.
For end to end tests, an integration test should be done.
If you want to pursue your integration test, use this: https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-2.2
If you really want to test your controller: https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/testing?view=aspnetcore-2.2
Upvotes: -1