使用CustomWebApplicationFactory进行Asp.Net核心集成测试-如何工作?

时间:2019-08-13 22:18:31

标签: c# asp.net-core

我试图更好地了解集成测试。默认情况下,很多示例都使用IClassFixture<T>进行集成测试(例如https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-2.2#basic-tests-with-the-default-webapplicationfactory)。

这对于测试类似的东西非常有用-是页面加载,正在显示的表单,我是否获得了正确的http状态代码等。但是在测试API时,您会希望存在一些种子数据。为了使种子数据进入测试,典型的选择是EF内存数据库。这是通过自定义Web应用程序工厂实现的,在该工厂中,您可以创建范围,请求适当的服务(即dbcontext)并将其作为种子(例如https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-2.2#customize-webapplicationfactory)。

我有一个正在运行且功能齐全的集成测试项目。但是,它如何工作的细微差别仍然让我感到困惑。


我正确的假设是,当您创建 CustomWebApplicationFactory 时,实际上是在创建自定义“ Program.cs”(即应用程序的典型入口点),您可以在其中自由添加是否需要其他测试服务/过滤器?


下面是我用于集成测试的自定义Web应用程序工厂。我的API对大多数端点都具有基本身份验证,因此我添加了一个全局过滤器来绕过它。但是我在下面的操作与实际API中的 Program.cs 基本相同(唯一的区别是我没有添加假用户和全局匿名过滤器)。因此,我相信我的上述观点是正确的。这是正确的假设吗?

我想验证的另一点是,在实际的单元测试中,我可以用模拟代替服务。在集成测试中这是否可行,在该测试中我可以将DI实例换成所请求的服务以成为测试服务?


例如我的应用程序具有IUploadFileToAzure服务。可以在集成测试中用TestUploadFileToAzure服务替换该实现,而不是使用UploadFileToAzure作为DI实例?


多次注册服务需要最后一次注册服务,因此我想知道是否可以将其用作上述解决方法。甚至推荐吗?我了解这无法达到测试服务的目的,但想验证是否可行。我尝试在本地进行测试,但无法正常工作。

public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<Startup>
    {
        protected override IWebHostBuilder CreateWebHostBuilder()
        {
            return WebHost
                .CreateDefaultBuilder<Startup>(new string[0])
                .ConfigureServices(services =>
                {
                    services.AddSingleton<IStartupFilter, AddCustomMiddlewareStartupFilter>();
                });
        }


        protected override void ConfigureWebHost(IWebHostBuilder builder)
        {
            builder
            .UseEnvironment("Development")
            .ConfigureServices(services =>
            {
                services.AddMvc(opt =>
                {
                    //add a global anonymous filter
                    opt.Filters.Add(new AllowAnonymousFilter());

                    //add a filter for adding a fake claimsprincipal so that the user service
                    //correctly identifies the user
                    opt.Filters.Add(new FakeClaimsPrincipalFilter(true, false));
                });

                services.AddEntityFrameworkInMemoryDatabase();

                // Create a new service provider.
                var provider = services
                    .AddEntityFrameworkInMemoryDatabase()
                    .BuildServiceProvider();

                // Add a database context using an in-memory 
                // database for testing.
                services.AddDbContext<AppDbContext>(options =>
                {
                    options.UseInMemoryDatabase("TestDb");
                    options.UseInternalServiceProvider(provider);
                });

                // Build the service provider.
                var sp = services.BuildServiceProvider();

                // Create a scope to obtain a reference to the database context 
                using (var scope = sp.CreateScope())
                {
                    var scopedServices = scope.ServiceProvider;
                    var apiDb = scopedServices.GetRequiredService<AppDbContext>();   

                    // Ensure the database is created.
                    apiDb.Database.EnsureCreated();
                }
            });
        }
    }

1 个答案:

答案 0 :(得分:1)

  

我正确的假设是,当您创建一个   CustomWebApplicationFactory,实际上是在创建自定义   “ Program.cs”(即应用程序的典型入口点),其中   您可以根据需要自由添加其他测试服务/过滤器吗?

是的,您是对的。对于Program.cs,它将创建真实的主机服务器。对于CustomWebApplicationFactory,它将为集成测试创建TestServer

  

我的应用程序具有IUploadFileToAzure服务。而不是使用   UploadFileToAzure作为DI实例,我可以替换它吗   在我的集成中使用TestUploadFileToAzure服务实现   测试?

要替换现有服务,您可以尝试ConfigureTestServices,也可以参考Inject mock services