[英]How to switch services in TestServer using WebApplicationFactory?
I'm using custom WebApplication factory我正在使用自定义 WebApplication 工厂
public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup: class {
protected override void ConfigureWebHost(IWebHostBuilder builder) {
builder.ConfigureServices(services => {
// Create a new service provider.
var serviceProvider = new ServiceCollection()
.AddEntityFrameworkInMemoryDatabase()
.BuildServiceProvider();
services.AddDbContext<GrabGoContext>(options => {
options.UseInMemoryDatabase("GrabGoDb");
options.UseInternalServiceProvider(serviceProvider);
});
services.AddSingleton<TestEmailServer>();
services.AddScoped<IEmailProvider, TestEmailProvider>(); // <- HERE
});
base.ConfigureWebHost(builder);
}
I want to switch my default IEmailProvider
service called DefaultEmailProvider
to my special TestEmailProvider
, but the problem is that the method ConfigureWebHost
is executed before Startup.ConfigureServices(IServiceCollection services)
, so my service DefaultEmailProvider
is set after TestEmailProvider
.我想将名为
DefaultEmailProvider
的默认IEmailProvider
服务切换到我的特殊TestEmailProvider
,但问题是方法ConfigureWebHost
是在Startup.ConfigureServices(IServiceCollection services)
之前执行的,所以我的服务DefaultEmailProvider
是在TestEmailProvider
之后设置的。 Therefore in my ClientController
service DeafultEmailProvider
is used instead of test service.因此,在我的
ClientController
服务中,使用了DeafultEmailProvider
而不是测试服务。
My question is:我的问题是:
How can I switch service DefaultEmailProvider
with my TestEmailProvider
using WebApplicationFactory?如何使用 WebApplicationFactory 将服务
DefaultEmailProvider
与我的TestEmailProvider
切换?
@Update @更新
Ok, I've managed to go deeper.好的,我已经设法深入 go。 I found that method
builder.ConfigureTestServices()
overrides other services.我发现方法
builder.ConfigureTestServices()
覆盖了其他服务。 But when switch this in my ConfigureWebHost(IWebHostBuilder builder)
method and I try to create new HttpClient using CreateClient()
it throws:但是,当在我的
ConfigureWebHost(IWebHostBuilder builder)
方法中切换它并尝试使用CreateClient()
创建新的 HttpClient 时,它会抛出:
System.InvalidOperationException: 'A ConfigureServices method that returns an IServiceProvider is not compatible with the use of one or more IStartupConfigureServicesFilter. Use a void returning ConfigureServices method instead or a ConfigureContainer method.'
@Update 28.05.2019 @更新 28.05.2019
Still looking for better solution , but I've manage to do sort of hack .仍在寻找更好的解决方案,但我已经设法做到了。
In my Startup.cs
I've swapped my在我的
Startup.cs
中,我交换了我的
services.AddScoped<IEmailProvider, SendGridEmailProvider>();
with TryAdd[Something]与 TryAdd[某事]
services.TryAddScoped<IEmailProvider, SendGridEmailProvider>();
You should be able to do it in the ConfigureWebHost similar to the below:您应该能够在类似于以下内容的 ConfigureWebHost 中执行此操作:
public class CustomWebApplicationFactory : WebApplicationFactory<Startup>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
// Remove application IEmailProvider service
var emailProviderDescriptor = services.SingleOrDefault(d => d.ServiceType == typeof(IEmailProvider));
if (emailProviderDescriptor != null)
{
services.Remove(emailProviderDescriptor);
}
// Add new test service(s)
services.AddScoped<IEmailProvider, TestEmailProvider>();
services.AddSingleton<TestEmailServer>(); // May need to remove using the descriptor similar to IEmailProvider
// Remove the app's GrabGoContext registration, add in memory one then seed data.
var dbDescriptor = services.SingleOrDefault(d => d.ServiceType == typeof(DbContextOptions<GrabGoContext>));
if (dbDescriptor != null)
{
services.Remove(dbDescriptor);
}
services.AddDbContext<GrabGoContext>(options =>
{
options.UseInMemoryDatabase("InMemoryDbForTesting");
});
var serviceProvider = services.BuildServiceProvider();
using var scope = serviceProvider.CreateScope();
var scopedServices = scope.ServiceProvider;
var grabGoContext = scopedServices.GetRequiredService<GrabGoContext>();
// Seed your db here & save..
grabGoContext.SaveChanges();
});
base.ConfigureWebHost(builder);
}
}
You may want to make it easier on yourself and just implement the factory, like the above, rather than keep it generic.您可能想让自己更轻松,只需实现工厂,就像上面一样,而不是保持通用。 I've also added slightly different code for your in-memory database.
我还为您的内存数据库添加了稍微不同的代码。
It hasn't been compiled, but the syntax shouldn't be far off.它尚未编译,但语法应该不远。 Hope that helps.
希望有帮助。
You can do it based on Environment.您可以根据环境进行操作。 This is just code to point you in the right direction.
这只是为您指明正确方向的代码。
public IHostingEnvironment HostingEnvironment { get; }
public void ConfigureServices(IServiceCollection services)
{
if (HostingEnvironment.IsDevelopment())
{
services.AddTransient<ITestService, TestService>();
}
else
{
services.AddTransient<IRealService, RealService>();
}
// other services
}
I hope this helps.我希望这有帮助。
I just spent several hours trying to do a similar thing (override the services in my integration tests).我只是花了几个小时试图做类似的事情(覆盖我的集成测试中的服务)。 Turns out
ConfigureServices
runs before the SUT (subject under test) service configuration.结果是
ConfigureServices
在SUT(被测对象)服务配置之前运行。 ConfigureTestServices
, however, runs after and is where you would override with your mocks.但是,
ConfigureTestServices
在之后运行,并且是您可以用模拟覆盖的地方。
The docs are not at all clear on this, though I did, after the fact, find this:文档对此一点也不明确,尽管我确实在事后找到了这一点:
https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-3.0#inject-mock-services https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-3.0#inject-mock-services
You can try this, call ConfigureTestServices rather than call ConfigureServices :你可以试试这个,调用ConfigureTestServices而不是调用ConfigureServices :
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder
.UseEnvironment("Test")
.ConfigureTestServices(services =>
{
// Put your logic here
}); ;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.