I have trouble with testing my application using Owin.TestServer
. I couldn't find anything useful, and I hope this is an easy fix that community can help with :)
Recently I started to write integration tests for my WebApi application that is using OWIN and AutoFac for DI. I have 3 integration tests in total. When I run each test individually, they are all passing. However when I run all of the tests at once, only first one succeeds and the other two are failed because of the following AutoFac error:
System.AggregateException: One or more errors occurred. --->
System.ObjectDisposedException: Instances cannot be resolved and nested lifetimes cannot be created from this LifetimeScope as it has already been disposed.
Stacktrace indicates that the error comes from the AutoFac middleware for Owin.
I have following setup for the tests:
[TestClass]
public class DinnerListControllerTests
{
private TestServer _server;
private TransactionScope _transactionScope;
[TestInitialize]
public void Init()
{
_transactionScope = new TransactionScope(TransactionScopeOption.RequiresNew);
_server = TestServer.Create<Startup>();
}
[TestCleanup]
public void Dispose()
{
_server?.Dispose();
_transactionScope?.Dispose();
}
[TestMethod]
public void GetAllLists()
{
var response = _server.HttpClient.GetAsync("/api/dinnerlists").Result;
response.IsSuccessStatusCode.Should().BeTrue("there should be no error");
var result = response.Content.ReadAsAsync<IEnumerable<DinnerListDTO>>().Result;
result.Should().NotBeNull().And.HaveCount(5);
}
[TestMethod]
public void GetActiveListsReturnsTwoLists()
{
var response = _server.HttpClient.GetAsync("/api/dinnerlists/active").Result;
response.IsSuccessStatusCode.Should().BeTrue();
var result = response.Content.ReadAsAsync<IEnumerable<DinnerListDTO>>().Result;
result.Should()
.NotBeNullOrEmpty()
.And.HaveCount(2)
.And.OnlyContain(dto => dto.OpenUntil.CompareTo(DateTime.Now) > 0);
}
}
GetAllLists
test will execute properly, but the second one will fail with message stated above.
I have tried with different registration scopes of the dependecies, but it didn't help. Below is my AutoFac configuration, startup class and sample AutoFac module:
AutoFacConfig.cs
public class AutoFacConfig
{
private static IContainer _container;
public static IContainer Container => _container ?? (_container = BuildContainer());
public static void ConfigureAutoFac(HttpConfiguration config)
{
if (config == null)
throw new ArgumentNullException(nameof(config));
FluentValidationModelValidatorProvider.Configure(config,
provider => provider.ValidatorFactory = new AutoFacValidatorFactory(Container));
config.DependencyResolver = new AutofacWebApiDependencyResolver(Container);
}
private static IContainer BuildContainer()
{
var autoFacBuilder = new ContainerBuilder();
var assembly = Assembly.GetExecutingAssembly();
autoFacBuilder.RegisterApiControllers(assembly).InstancePerRequest();
autoFacBuilder.RegisterType<DinnerDbContext>().InstancePerRequest();
autoFacBuilder.RegisterModule<RepositoryModule>();
autoFacBuilder.RegisterModule<ServicesModule>();
autoFacBuilder.RegisterModule<ValidationModule>();
autoFacBuilder.RegisterModule<AutoMapperModule>();
autoFacBuilder.RegisterModule<AutofacWebTypesModule>();
return autoFacBuilder.Build();
}
}
Startup.cs:
public class Startup
{
public void Configuration(IAppBuilder appBuilder)
{
var httpConfiguration = new HttpConfiguration();
WebApiConfig.Register(httpConfiguration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
AutoFacConfig.ConfigureAutoFac(httpConfiguration);
AutoMapperConfig.RegisterMappings();
appBuilder.UseAutofacMiddleware(AutoFacConfig.Container);
appBuilder.UseAutofacWebApi(httpConfiguration);
appBuilder.UseWebApi(httpConfiguration);
}
}
Sample AutoFac module:
public class RepositoryModule : Module
{
protected override void Load(ContainerBuilder builder)
{
var assembly = System.Reflection.Assembly.GetExecutingAssembly();
builder.RegisterAssemblyTypes(assembly)
.Where(type => type.Name.EndsWith("Repository"))
.AsImplementedInterfaces()
.InstancePerRequest();
}
}
EDIT - SOLUTION
What @Eris suggested made a lot of sense - my AutoFacConfig
class was using static methods and members, which meant that the Container
property was present on subsequent tests and it was not being created again and it was marked as disposed.
I decided to refactor the code so AutoFacConfig
is not using static members any more, as I didn't want to play with disposal of the container on application shutdown.
AutoFacConfig.cs:
public class AutoFacConfig
{
private IContainer _container;
public IContainer Container
{
get { return _container ?? (_container = BuildContainer()); }
}
public void ConfigureAutoFac(HttpConfiguration config)
{
//...
}
private IContainer BuildContainer()
{
var autoFacBuilder = new ContainerBuilder();
//...
return autoFacBuilder.Build();
}
}
Startup.cs
public class Startup
{
public void Configuration(IAppBuilder appBuilder)
{
var httpConfiguration = new HttpConfiguration();
var autofacConfig = new AutoFacConfig(); // create instance of AutoFacConfig
autofacConfig.ConfigureAutoFac(httpConfiguration); // configure autofac
appBuilder.UseAutofacMiddleware(autofacConfig.Container);
appBuilder.UseAutofacWebApi(httpConfiguration);
appBuilder.UseWebApi(httpConfiguration);
}
}