У меня проблемы с тестированием моего приложения, используя Owin.TestServer
. Я не мог найти ничего полезного, и я надеюсь, что это легко исправить, что сообщество может помочь с:)WebApi2 с OWIN TestServer и AutoFac - LifetimeScope уже установлен
Недавно я начал писать интеграционные тесты для моего приложения WebApi, использующего OWIN и AutoFac для DI. У меня всего 3 интеграционных теста. Когда я запускаю каждый тест по отдельности, все они проходят. Однако, когда я бегу все тесты сразу, только первый один преуспевает и другие два потерпели неудачу из-за следующей ошибки AutoFac:
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 указывает, что ошибка происходит из промежуточного AutoFac для Owin.
У меня после установки для испытаний:
[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
тест будет выполняться правильно, но второй один потерпит неудачу с сообщением указанного выше.
Я пробовал с различными регистрационными областями зависимостей, но это не помогло. Ниже моя конфигурация AutoFac, класс запуска и образец AutoFac модуль:
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);
}
}
Пример модуля AutoFac:
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 - РЕШЕНИЕ
Что @Eris предложил сделал много смысла - мой AutoFacConfig
класса с использованием статических методов и членов, а это означало, что Container
свойства присутствовало на последующих тестах и не создаются вновь, и это было помечены как расположенные.
Я решил реорганизовать код, поэтому AutoFacConfig
больше не использует статические элементы, так как я не хотел играть с удалением контейнера при завершении работы приложения.
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);
}
}
Если вы вызываете два метода в одном, будет вы получаете ошибку? –
есть ли конкретная причина, по которой вы воссоздаете '_server' и' _transactionscope' для каждого теста, в отличие от одного сервера на каждое тестовое оборудование, сборку и т. Д.? – Eris
@ Эрис. Я думал, что это так, особенно с областью транзакций, чтобы поддерживать соответствие данных между тестами, поскольку некоторые из них управляют базой данных. – jjczopek