在 Web 集成测试中使用 Autofac api 2
Using Autofac in integration tests with web api 2
我一直在我的集成测试中手动实例化我的服务,但是当我得到一个具有 Lazy 依赖项的服务时,我做了一些研究并发现 you can actually use Autofac to resolve your services when doing your tests.
所以,我写了这个class:
public class Container<TModule> where TModule: IModule, new()
{
private readonly IContainer _container;
protected Container()
{
var builder = new ContainerBuilder();
builder.RegisterModule(new TModule());
_container = builder.Build();
}
protected TEntity Resolve<TEntity>() => _container.Resolve<TEntity>();
protected void Dispose() => _container.Dispose();
}
然后在我的上下文中,我更改为:
public class ProductContext : Container<AutofacModule>
{
public IProductProvider ProductProvider { get; }
public static ProductContext GiventServices() => new ProductContext();
protected ProductContext()
{
ProductProvider = Resolve<IProductProvider>();
}
public List<JObject> WhenListProducts(int categoryId) => ProductProvider.List(categoryId);
}
我有另一个似乎有效的上下文(测试通过)并且正在使用 MatchProvider。如果我在我的 Autofac 模块中比较两者,它们看起来像这样:
builder.RegisterType<ProductProvider>().As<IProductProvider>().InstancePerRequest();
和
builder.RegisterType<MatchProvider>().As<IMatchProvider>().SingleInstance();
因为 MatchProvider 是一个单例,它似乎没有解决任何问题,但是 ProductProvider 是每个请求的一个实例,这似乎是问题所在。
当 运行 需要该服务的任何测试时,我收到此错误:
No scope with a tag matching 'AutofacWebRequest' is visible from the scope in which the instance was requested.
我想这是因为我没有安装正确的 nuget 包。所以我安装了:
- Autofac
- Autofac.Integration.Owin
- Autofac.Integration.WebApi
- Autofac.Integration.WebApi.欧文
这些是在定义我的模块时使用的相同引用,但这没有帮助。
有谁知道我需要做什么才能让它工作?
我找不到合适的(简单的)解决方案。我看到有些人自己创建生命周期范围,这对我来说似乎有点矫枉过正而且它不是 "nice" 代码。
因此,采用 Autofac 的 原则之一:多次注册的任何服务;最后一个实例是已解决的实例。
所以在我的 Container
class 中,我只是将我的 InstancePerRequest 服务重新注册为 InstancePerDependency .这解决了我的问题。
这是我的完整代码:
public class ContainerContext<TModule> where TModule: IModule, new()
{
private IContainer _container;
protected ContainerContext()
{
var builder = new ContainerBuilder();
builder.RegisterModule(new TModule());
// Because Autofac will resolve services using the last registration, we can change all our web api
// services to by InstancePerDependency instead of InstancePerRequest which is obviously better
// when testing.
builder.RegisterType<AnswerProvider>().As<IAnswerProvider>().InstancePerDependency();
builder.RegisterType<AttributeProvider>().As<IAttributeProvider>().InstancePerDependency();
builder.RegisterType<CategoryProvider>().As<ICategoryProvider>().InstancePerDependency();
builder.RegisterType<ClaimProvider>().As<IClaimProvider>().InstancePerDependency();
builder.RegisterType<ClientProvider>().As<IClientProvider>().InstancePerDependency();
builder.RegisterType<CriteriaProvider>().As<ICriteriaProvider>().InstancePerDependency();
builder.RegisterType<FeedProvider>().As<IFeedProvider>().InstancePerDependency();
builder.RegisterType<FormulaProvider>().As<IFormulaProvider>().InstancePerDependency();
builder.RegisterType<ImageProvider>().As<IImageProvider>().InstancePerDependency();
builder.RegisterType<GroupProvider>().As<IGroupProvider>().InstancePerDependency();
builder.RegisterType<QuestionProvider>().As<IQuestionProvider>().InstancePerDependency();
builder.RegisterType<StripeProvider>().As<IStripeProvider>().InstancePerDependency();
builder.RegisterType<ApiAiProvider>().As<IApiAiProvider>().InstancePerDependency();
builder.RegisterType<PiiikProvider>().As<IPiiikProvider>().InstancePerDependency();
builder.RegisterType<ProductProvider>().As<IProductProvider>().InstancePerDependency();
builder.RegisterType<SettingProvider>().As<ISettingProvider>().InstancePerDependency();
builder.RegisterType<TrackingProvider>().As<ITrackingProvider>().InstancePerDependency();
_container = builder.Build();
}
protected TEntity Resolve<TEntity>() => _container.Resolve<TEntity>();
protected void Dispose() => _container.Dispose();
}
然后,我使用的任何上下文都会继承此 class:
public class ProductContext : ContainerContext<AutofacModule>
{
public IProductProvider ProductProvider { get; }
public static ProductContext GiventServices() => new ProductContext();
protected ProductContext()
{
ProductProvider = Resolve<IProductProvider>();
}
public List<JObject> WhenListProducts(int categoryId) => ProductProvider.List(categoryId);
}
这意味着,在测试时,我可以这样做:
[TestFixture]
public class ProductProviderTests
{
[Test]
public void ShouldHaveProducts()
{
var services = ProductContext.GiventServices();
var products = services.WhenListProducts(1);
products.Count.Should().NotBe(0);
}
[Test]
public void ShouldHaveDuplicateVariants()
{
var services = ProductContext.GiventServices();
var products = services.WhenListProducts(1);
var anyDuplicate = products.GroupBy(x => x.SelectToken("variant").ToString()).Any(g => g.Count() > 1);
anyDuplicate.Should().Be(true);
}
[Test]
public void ShouldNotHaveDuplicateGtins()
{
var services = ProductContext.GiventServices();
var products = services.WhenListProducts(1);
var anyDuplicate = products.GroupBy(x => x.SelectToken("gtin").ToString()).Any(g => g.Count() > 1);
anyDuplicate.Should().Be(false);
}
}
这应该可以帮助遇到同样问题的其他人。
我一直在我的集成测试中手动实例化我的服务,但是当我得到一个具有 Lazy 依赖项的服务时,我做了一些研究并发现 you can actually use Autofac to resolve your services when doing your tests.
所以,我写了这个class:
public class Container<TModule> where TModule: IModule, new()
{
private readonly IContainer _container;
protected Container()
{
var builder = new ContainerBuilder();
builder.RegisterModule(new TModule());
_container = builder.Build();
}
protected TEntity Resolve<TEntity>() => _container.Resolve<TEntity>();
protected void Dispose() => _container.Dispose();
}
然后在我的上下文中,我更改为:
public class ProductContext : Container<AutofacModule>
{
public IProductProvider ProductProvider { get; }
public static ProductContext GiventServices() => new ProductContext();
protected ProductContext()
{
ProductProvider = Resolve<IProductProvider>();
}
public List<JObject> WhenListProducts(int categoryId) => ProductProvider.List(categoryId);
}
我有另一个似乎有效的上下文(测试通过)并且正在使用 MatchProvider。如果我在我的 Autofac 模块中比较两者,它们看起来像这样:
builder.RegisterType<ProductProvider>().As<IProductProvider>().InstancePerRequest();
和
builder.RegisterType<MatchProvider>().As<IMatchProvider>().SingleInstance();
因为 MatchProvider 是一个单例,它似乎没有解决任何问题,但是 ProductProvider 是每个请求的一个实例,这似乎是问题所在。
当 运行 需要该服务的任何测试时,我收到此错误:
No scope with a tag matching 'AutofacWebRequest' is visible from the scope in which the instance was requested.
我想这是因为我没有安装正确的 nuget 包。所以我安装了:
- Autofac
- Autofac.Integration.Owin
- Autofac.Integration.WebApi
- Autofac.Integration.WebApi.欧文
这些是在定义我的模块时使用的相同引用,但这没有帮助。 有谁知道我需要做什么才能让它工作?
我找不到合适的(简单的)解决方案。我看到有些人自己创建生命周期范围,这对我来说似乎有点矫枉过正而且它不是 "nice" 代码。 因此,采用 Autofac 的 原则之一:多次注册的任何服务;最后一个实例是已解决的实例。
所以在我的 Container
class 中,我只是将我的 InstancePerRequest 服务重新注册为 InstancePerDependency .这解决了我的问题。
这是我的完整代码:
public class ContainerContext<TModule> where TModule: IModule, new()
{
private IContainer _container;
protected ContainerContext()
{
var builder = new ContainerBuilder();
builder.RegisterModule(new TModule());
// Because Autofac will resolve services using the last registration, we can change all our web api
// services to by InstancePerDependency instead of InstancePerRequest which is obviously better
// when testing.
builder.RegisterType<AnswerProvider>().As<IAnswerProvider>().InstancePerDependency();
builder.RegisterType<AttributeProvider>().As<IAttributeProvider>().InstancePerDependency();
builder.RegisterType<CategoryProvider>().As<ICategoryProvider>().InstancePerDependency();
builder.RegisterType<ClaimProvider>().As<IClaimProvider>().InstancePerDependency();
builder.RegisterType<ClientProvider>().As<IClientProvider>().InstancePerDependency();
builder.RegisterType<CriteriaProvider>().As<ICriteriaProvider>().InstancePerDependency();
builder.RegisterType<FeedProvider>().As<IFeedProvider>().InstancePerDependency();
builder.RegisterType<FormulaProvider>().As<IFormulaProvider>().InstancePerDependency();
builder.RegisterType<ImageProvider>().As<IImageProvider>().InstancePerDependency();
builder.RegisterType<GroupProvider>().As<IGroupProvider>().InstancePerDependency();
builder.RegisterType<QuestionProvider>().As<IQuestionProvider>().InstancePerDependency();
builder.RegisterType<StripeProvider>().As<IStripeProvider>().InstancePerDependency();
builder.RegisterType<ApiAiProvider>().As<IApiAiProvider>().InstancePerDependency();
builder.RegisterType<PiiikProvider>().As<IPiiikProvider>().InstancePerDependency();
builder.RegisterType<ProductProvider>().As<IProductProvider>().InstancePerDependency();
builder.RegisterType<SettingProvider>().As<ISettingProvider>().InstancePerDependency();
builder.RegisterType<TrackingProvider>().As<ITrackingProvider>().InstancePerDependency();
_container = builder.Build();
}
protected TEntity Resolve<TEntity>() => _container.Resolve<TEntity>();
protected void Dispose() => _container.Dispose();
}
然后,我使用的任何上下文都会继承此 class:
public class ProductContext : ContainerContext<AutofacModule>
{
public IProductProvider ProductProvider { get; }
public static ProductContext GiventServices() => new ProductContext();
protected ProductContext()
{
ProductProvider = Resolve<IProductProvider>();
}
public List<JObject> WhenListProducts(int categoryId) => ProductProvider.List(categoryId);
}
这意味着,在测试时,我可以这样做:
[TestFixture]
public class ProductProviderTests
{
[Test]
public void ShouldHaveProducts()
{
var services = ProductContext.GiventServices();
var products = services.WhenListProducts(1);
products.Count.Should().NotBe(0);
}
[Test]
public void ShouldHaveDuplicateVariants()
{
var services = ProductContext.GiventServices();
var products = services.WhenListProducts(1);
var anyDuplicate = products.GroupBy(x => x.SelectToken("variant").ToString()).Any(g => g.Count() > 1);
anyDuplicate.Should().Be(true);
}
[Test]
public void ShouldNotHaveDuplicateGtins()
{
var services = ProductContext.GiventServices();
var products = services.WhenListProducts(1);
var anyDuplicate = products.GroupBy(x => x.SelectToken("gtin").ToString()).Any(g => g.Count() > 1);
anyDuplicate.Should().Be(false);
}
}
这应该可以帮助遇到同样问题的其他人。