前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ASP.NET Core 中做集成测试的三种方案

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

作者头像
老张的哲学
发布2022-04-11 16:25:51
8640
发布2022-04-11 16:25:51
举报
文章被收录于专栏:NetCore 从壹开始

学习·进步

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

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

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

方案一:万物皆可Mock

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

代码语言:javascript
复制
 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控制器呢,示例代码如下:

代码语言:javascript
复制
 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请求即可:

代码语言:javascript
复制
  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做了返回值处理:

代码语言:javascript
复制
 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方式创建的,示例代码如下:

代码语言:javascript
复制
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特别像,然后使用起来就更加简单了:

代码语言:javascript
复制
 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依赖注入容器没有做任何的修改。

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

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-07-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 NetCore 从壹开始 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档