如何使用WebApplicationFactory在TestServer中切换服务?

时间:2019-05-09 13:45:19

标签: c# asp.net-core xunit

我正在使用自定义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);
    }

我想将名为IEmailProvider的默认DefaultEmailProvider服务切换到特殊的TestEmailProvider,但是问题是方法ConfigureWebHostStartup.ConfigureServices(IServiceCollection services)之前执行,因此我的服务DefaultEmailProvider设置在TestEmailProvider之后。因此,在我的ClientController服务中,DeafultEmailProvider代替了测试服务。

我的问题是:

如何使用WebApplicationFactory与DefaultEmailProvider切换服务TestEmailProvider

@更新

好的,我已经更深入了。我发现方法builder.ConfigureTestServices()覆盖了其他服务。但是,当在我的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.'

@更新28.05.2019

仍在寻找更好的解决方案,但我设法做了一些 hack

在我的Startup.cs中,我交换了

services.AddScoped<IEmailProvider, SendGridEmailProvider>();

使用TryAdd [功能]

services.TryAddScoped<IEmailProvider, SendGridEmailProvider>();

3 个答案:

答案 0 :(得分:1)

您可以根据环境进行操作。这只是将您指向正确方向的代码。

  public IHostingEnvironment HostingEnvironment { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        if (HostingEnvironment.IsDevelopment())
        {
            services.AddTransient<ITestService, TestService>();
        }
        else
        {
            services.AddTransient<IRealService, RealService>();
        }

        // other services
    }

我希望这会有所帮助。

答案 1 :(得分:1)

我花了几个小时尝试做类似的事情(在集成测试中覆盖了服务)。原来ConfigureServices在SUT(被测对象)服务配置之前 运行。 ConfigureTestServices,但是,在之后运行,并且是您在其中覆盖模拟内容的地方。

尽管对此我确实确实发现了以下内容,但文档并不清楚:

https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-3.0#inject-mock-services

答案 2 :(得分:1)

您应该能够在ConfigureWebHost中执行以下操作:

for-loop

您可能想使它更容易使用,只是像上面那样实现工厂,而不是使它通用。我还为您的内存数据库添加了略有不同的代码。

它还没有被编译,但是语法应该相距不远。 希望有帮助。