ASP.NET Core 中做集成测试的三种方案

Posted dotNET跨平台

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ASP.NET Core 中做集成测试的三种方案相关的知识,希望对你有一定的参考价值。

学习·进步

老张的哲学

不定期更新的

日常

  在平时的开发中,我们很少会关注到测试的问题,更别说集成测试了,除非是公司有硬性要求或者是自己的开源项目中,为了整体架构的完整性,需要用测试来做辅助点缀,而更多的也仅仅是单元测试(说的就是我自己????),最近在写书的时候才进一步考虑到这一点,如何在一个ASP.NET Core框架中,引入集成测试呢?

  这里我结合这三年开源的经验,总结了一些心得,给大家分享一下,如果有更好的建议,欢迎在评论区进行留言哟。

PS:单元测试就不说了,比较简单,最多就是依赖注入和MOCK的问题,不会的话也可以留言。

方案一:万物皆可Mock

  在软件测试当中,我们经常,甚至是到处都会用到mock来处理对象实例化的问题,在单元测试中,mock十分常见,毕竟是为了测试一个小模块,其他的就不需要考虑,直接mock就行了,如果在集成测试的时候,如何测试接口呢,比如BlogController如何使用?我在blog.core项目中,就是这么使用到的,示例代码如下:

 Mock<IBlogArticleServices> mockBlogSev = new Mock<IBlogArticleServices>();
 Mock<ILogger<BlogController>> mockLogger = new Mock<ILogger<BlogController>>();
 BlogController blogController;


 private IBlogArticleServices blogArticleServices;
 DI_Test dI_Test = new DI_Test();






 public BlogController_Should()
 {
     mockBlogSev.Setup(r => r.Query());




     var container = dI_Test.DICollections();
     blogArticleServices = container.Resolve<IBlogArticleServices>();
     blogController = new BlogController(mockLogger.Object);
 }




  说句实话,这并非是集成测试,这种写法可能比较低端,通过mock配合new,创建了控制器,然后调用接口,看起来不是很高大上,而且集成测试本来就是要测试整体性,不能把所有的参数都mock吧。同时官方好像也说过,不要到处使用mock。

而且,这种方案,也要考虑如何使用依赖注入的问题!

所以这种方案做集成测试我给:
⭐⭐

方案二:实例化TestServer对象

  这种是比较常见的,也是微软官方架构项目eShopOnContainers的推荐方案,简单来说,就是微软提供了一个TestSever的类,为我们提供一个类似WebHost的宿主服务器,只不过是测试服务器,那如何测试Controller控制器呢,示例代码如下:

 public TestServer CreateServer()
 {
     var path = Assembly.GetAssembly(typeof(CatalogScenariosBase))
       .Location;


     var hostBuilder = new WebHostBuilder()
         .UseContentRoot(Path.GetDirectoryName(path))
         .ConfigureAppConfiguration(cb =>
         {
             cb.AddJsonFile("appsettings.json", optional: false)
             .AddEnvironmentVariables();
         })
         .UseStartup<Startup>();




     var testServer = new TestServer(hostBuilder);


     testServer.Host
         .MigrateDbContext<CatalogContext>((context, services) =>
         {
             var env = services.GetService<IWebHostEnvironment>();
             var settings = services.GetService<IOptions<CatalogSettings>>();
             var logger = services.GetService<ILogger<CatalogContextSeed>>();


             new CatalogContextSeed()
             .SeedAsync(context, env, settings, logger)
             .Wait();
         })
         .MigrateDbContext<IntegrationEventLogContext>((_, __) => { });


     return testServer;
 }


  可以看到,通过new TestServer()的方式,生成一个服务器,就可以发起请求了,核心的还是我们的WebHostBuilder。

至于如何调用就更简单了,直接对server发起HttpClient请求即可:

  using (var server = CreateServer())
  {
      var response = await server.CreateClient()
          .GetAsync(Get.ItemById(1));


      response.EnsureSuccessStatusCode();
  }



  这种是很简单的,而且也不用考虑mock的问题,毕竟用的直接就是web项目的WebHost宿主机Builder来构建的。

  但是有一个很致命的问题,我们在.NET5以后,使用Autofac做依赖注入的容器,而且ConfigureServices也是没有返回值的,这样在使用上面的TestServer,就会报错,提示找不到Autofac服务。

  但是如果你查看eShopOnContainers的源码后,就知道他们还是将ConfigureServices做了返回值处理:

 public IServiceProvider ConfigureServices(IServiceCollection servic
 {
     // 自定义服务扩展
     services.AddAppInsight(Configuration)
         // and so on...
         .AddCustomMVC(Configuration);


     // 使用Autofac依赖注入容器
     var container = new ContainerBuilder();
     container.Populate(services);


     return new AutofacServiceProvider(container.Build());
 }


  如果你能接受这种依赖注入的方式的话,也是可以使用这种方案的,这是一个注意点,要知道。

所以这种方案做集成测试我给:
⭐⭐⭐⭐

方案三:使用.UseTestServer()

  除了上面的这种方式,还有一种方式,也是官方提供的,比较类似,也是通过创建宿主机服务器的形式,不过是新的HostBuilder的ConfigureWebHostDefaults方式创建的,示例代码如下:

public static IHostBuilder GetTestHost()
{
    return new HostBuilder()
            //替换autofac作为DI容器
            .UseServiceProviderFactory(new AutofacServiceProviderFactory())
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder
                .UseTestServer()
                .UseStartup<Startup>();
            })
            .ConfigureAppConfiguration((host, builder) =>
            {
                builder.SetBasePath(Directory.GetCurrentDirectory());
                builder.AddJsonFile("appsettings.json", optional: true);
                builder.AddEnvironmentVariables();
            });
}


  既然上面说了我们不能单独处理自定义容器,我们就和之前一样,指定就好,设计思路和我们的WebApi中的Program.cs特别像,然后使用起来就更加简单了:

 using var server = await ArticleScenariosBase.GetTestHost().StartAsync();


 // Action 发起接口请求
 var response = await server.GetTestClientWithToken()
     .GetAsync("/api/blogs?page=1&pageSize=5");


 // Assert 确保接口状态码是200
 response.EnsureSuccessStatusCode();


  这种方案不仅兼容了第二种方案的优点,而且对之前我们设计的Autofac依赖注入容器没有做任何的修改。

所以这种方案做集成测试我给:
⭐⭐⭐⭐⭐

编者按:Blog.Core开源三周年

【原料】

 个人开源项目Blog.Core马上就已经开源三周年了,经过许许多多的小伙伴功能努力的结果,希望给ASP.NET Core在国内的推广,提供一个落地级别的案例。

【制法】

 A、累计提交上千次Commit;

 B、配合前、后、认证、鉴权一体化方案;

 C、不完全统计,被60+公司使用中;

【调味】

1.希望更多的小伙伴参与并提交PR。

2.希望更多的公司和组织使用,提供宝贵生产意见。

3.希望可以得到组织的孵化,让项目更进一步,有意者可以联系我

Tips: 九月新内容,敬请期待。

HAPPY EVEY DAY!

以上是关于ASP.NET Core 中做集成测试的三种方案的主要内容,如果未能解决你的问题,请参考以下文章

使用 .NET Framework 集成测试 ASP.NET Core - 找不到 deps.json

学习 ASP.NET Core 2.1:集成测试中使用 WebApplicationFactory

不得不说的 ASP.NET Core 集成测试

不得不说的 ASP.NET Core 集成测试

不得不说的 ASP.NET Core 集成测试

ASP.NET Core运行两个TestServer进行集成测试