In my integration tests I'm using the same SimpleInjector.Container which I construct in the Web API project I'm testing.
But this line in composition root class:
container.RegisterWebApiControllers(GlobalConfiguration.Configuration);
causes an exception:
System.TypeInitializationException : The type initializer for 'MyProject.Api.Test.Integration.HttpClientFactory' threw an exception.
---- System.InvalidOperationException : This method cannot be called during the application's pre-start initialization phase.
Result StackTrace:
at MyProject.Api.Test.Integration.HttpClientFactory.Create()
at MyProject.Api.Test.Integration.Controllers.ProductControllerIntegrationTest.<GetProductBarcode_Should_Return_Status_BadRequest_When_Barcode_Is_Empty>d__0.MoveNext() in d:\Projects\My\MyProject.Api.Test.Integration\Controllers\ProductControllerIntegrationTest.cs:line 26
----- Inner Stack Trace -----
at System.Web.Compilation.BuildManager.EnsureTopLevelFilesCompiled()
at System.Web.Compilation.BuildManager.GetReferencedAssemblies()
at System.Web.Http.WebHost.WebHostAssembliesResolver.System.Web.Http.Dispatcher.IAssembliesResolver.GetAssemblies()
at System.Web.Http.Dispatcher.DefaultHttpControllerTypeResolver.GetControllerTypes(IAssembliesResolver assembliesResolver)
at System.Web.Http.WebHost.WebHostHttpControllerTypeResolver.GetControllerTypes(IAssembliesResolver assembliesResolver)
at SimpleInjector.SimpleInjectorWebApiExtensions.GetControllerTypesFromConfiguration(HttpConfiguration configuration)
at SimpleInjector.SimpleInjectorWebApiExtensions.RegisterWebApiControllers(Container container, HttpConfiguration configuration)
at MyProject.Api.ContainerConfig.RegisterTypes(Container container) in d:\Projects\My\MyProject.Api\App_Start\ContainerConfig.cs:line 128
at MyProject.Api.ContainerConfig.CreateWebApiContainer() in d:\Projects\My\MyProject.Api\App_Start\ContainerConfig.cs:line 63
at MyProject.Api.Test.Integration.HttpClientFactory..cctor() in d:\Projects\My\MyProject.Api.Test.Integration\HttpClientFactory.cs:line 17
After commenting it everything works fine, both the web app itself and the tests.
So the question is:
Here's the code for HttpClientFactory (a helper class to create HttpClient with proper headers, such as api key or authorization):
internal static class HttpClientFactory
{
private static readonly Container _container = ContainerConfig.CreateWebApiContainer();
public static HttpClient Create()
{
var client = new HttpClient { BaseAddress = GetUrl() };
//...
return client;
}
}
If we look closely at the stack trace we can exactly see what is going on here. The RegisterWebApiControllers
extension method calls the GetControllerTypes
method on the IHttpControllerTypeResolver
instance it grabs from the HttpConfiguration
and it passes the IAssembliesResolver
that is also retrieved from the configuration. The called GetControllerTypes
method (of the WebHostHttpControllerTypeResolver
) calls into the GetControllerTypes
of the DefaultHttpControllerTypeResolver
which will eventually cause a call to GetReferencedAssemblies
of the System.Web.Compilation.BuildManager
class.
The System.Web.Compilation.BuildManager
however, can not be called early in the ASP.NET pipeline, or outside the context of ASP.NET at all. Since you're in a test, the BuildManage
will throw the exception you are experiencing.
So the solution (or 'trick' so you will) here is to replace the default IAssembliesResolver
when unit testing. I imagine that resolver to look like this:
public class TestAssembliesResolver : IAssembliesResolver
{
public ICollection<Assembly> GetAssemblies()
{
return AppDomain.CurrentDomain.GetAssemblies();
}
}
[TestMethod]
public void TestMethod1()
{
// Replace the original IAssembliesResolver.
GlobalConfiguration.Configuration.Services.Replace(typeof(IAssembliesResolver),
new TestAssembliesResolver());
var container = SimpleInjectorWebApiInitializer.BuildContainer();
container.Verify();
}
It's a bit unfortunate that you have to deal this, especially since Simple Injector was designed to be testable. It seems that we overlooked this by integrating the RegisterWebApiControllers
extension method so deeply with Web API. We have to take a step back and think about how to make it easier to verify the Web API configuration inside a unit test.