首次在WebAPI中写单元测试
xUnit
这次我使用的是xUnit
测试框架,而不是VS自带的MSTest
框架。在添加新建项目时选择xUnit测试项目就行了。
目前只体验到了一个差别,即xUnit可以使用特性向测试方法传参,而不用在测试方法中一个赋值语句一个个去定义参数,这是比较方便的。
单元测试有一个好处,就是一次性可以获得所测试的很多接口的失败信息。如果使用swagger
去测试接口,只能去启动项目,输入密码鉴权,然后一个个发请求。遇到一个错误处理一个接口,比较麻烦。单元测试可以把所有接口的报错信息展示在测试窗口,而且是缓存的,不用启动项目,只需要点击一下测试按钮,就把所有接口测试了。我在迁移接口时,有一个控制器一次性迁移了14个接口,单元测试通过了6个,失败了8个,失败的都列出了错误消息,这就很舒服了。经过了几天的使用,我发现这比到swagger或postman中手动测试接口方便太多了。
这里的失败基本都是迁移数据库结构引起的,我申请修改结构后,就又有几个通过了测试。修改进度如何,看起来很直观。到目前为止几天了,测试仍然没有全部通过,单元测试起到了很好的监控作用。
点击失败的测试,可以看到调用堆栈,跳转到运行失败的那一行代码。这使得修改起来很方便。
单元测试环境准备
我写的是控制器方法中action
的单元测试。但是一般来说,控制器和Service
层会注入许多服务,而action依赖于这些服务。在使用依赖注入时,单元测试要如何处理这种情况?
可以和ASP.NET core
的做法一样。它准备了一个依赖注入容器,那我也准备一个依赖注入容器。WebAPI还构造了一个web主机。但是单元测试是独立运行的,就不需要创建一个web主机了。在单元测试项目种添加了一个TestBase基类
,用于创建容器,注册服务,以供测试方法使用。
//测试环境
public class TestBase
{
//依赖注入容器
public IServiceCollection Services;
//从容器获取服务
public IServiceProvider Provider;
public TestBase()
{
//创建容器
Services = new ServiceCollection();
//....注册服务
Provider = Services.BuildServiceProvider();
}
}
然后向容器注册我们需要的服务,比如常见的MemoryCache
IWebHostEnvironment
ISqlSugarClient
XXXService
。我们就不需要在测试方法中使用new
运算符创建服务类实例,而是可以直接从容器中获得了。
//注册服务层
Services.AddScoped<ISingleWellService, SingleWellService>();
//注册缓存
Services.AddScoped<IMemoryCache, MemoryCache>(service =>
{
return new MemoryCache(new MemoryCacheOptions());
});
//注册SqlSuger
Services.AddScoped<ISqlSugarClient>(service =>
{
return new SqlSugarClient(new ConnectionConfig()
{
ConnectionString = "Data Source=XXX",
DbType = DbType.Oracle,
IsAutoCloseConnection = true,
InitKeyType = InitKeyType.Attribute,
});
});
//注册环境变量
Services.AddScoped<IWebHostEnvironment, WebHostEnvironment>();
IWebHostEnvironment
为了使用这个接口需要引入包Microsoft.Extensions.DependencyInjection.Abstractions
。这个接口一般是WebApplicationBuilder创建的,在涉及文件读写时经常用到。但是单元测试项目中没有builder,我就自己添加了一个IWebHostEnvironment实现类,并注册到容器中。缺点是还要把可能需要的WebAPI项目中的文件,比如模板文件、数据文件也复制到单元测试项目中。
public class WebHostEnvironment : IWebHostEnvironment
{
public string WebRootPath { get => Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "wwwroot"); }
public IFileProvider WebRootFileProvider { get; set; }
public string EnvironmentName { get; set; }
public string ApplicationName { get; set; }
public string ContentRootPath { get => AppDomain.CurrentDomain.BaseDirectory; }
public IFileProvider ContentRootFileProvider { get; set; }
}
ICurentUser
这个自定义接口一般是在请求处理管道中存储身份验证后的相关信息。单元测试中不同的接口可能需要不同的user,比如一个流程中,不同角色调用同一个接口。ICurentUser同样也是注册到容器中的,然后在service层注入。具体的业务方法中根据这个角色的不同执行不同逻辑。
我比较疑惑的是,单元测试又该怎么注入呢?要注意的是,不同测试方法的ICurentUser是不同的。要知道从容器中获取service,容器自动帮我们挑选了合适够构造方法。但是这里由于角色的不同,不能直接从容器获取准备好的角色存根。难道我们要手动构造service传入controller中吗?我是有听说mokq
,但不知道怎么用来模拟多个ICurentUser。
为控制器添加单元测试
添加一个HomeControllerUnitTest
类,并继承于前面定义的基类TestBase
。我们应该在构造函数中从容器取出相应的服务以供使用。
public class HomeControllerUnitTest:TestBase
{
HomeController homeController;
public HomeControllerUnitTest()
{
//从容器注入服务
homeController = new HomeController(
Provider.GetRequiredService<IHomeService>(),
Provider.GetRequiredService<IWebHostEnvironment>());
}
}
接着,向测试类添加单元Action
的测试方法。一般都是三步
- 准备数据 Arrange
- 调用测试方法 Act
- 断言结果 Assert
也就是AAA模式。
[Theory(DisplayName = "测试XXX")]
[InlineData("xxx", "xxx", 1,30)]
public void Test_GetData(string wId, string tId, int page, int rows)
{
var data = homeController.GetData(wId, tId, page, rows).Result;
Assert.True(data.Tag);
}
在实际使用时,我会每添加一个测试,就运行未运行的测试。头一天看一下哪些测试没通过,这里一般是数据库结构不对,然后申请修改数据库。第二天再运行失败的测试验证。不会每次运行全部测试。
首次在WebAPI中写单元测试的更多相关文章
- 【快学springboot】在springboot中写单元测试[Happyjava]
前言 很多公司都有写单元测试的硬性要求,在提交代码的时候,如果单测通不过或者说单元测试各种覆盖率不达标,会被拒绝合并代码.写单元测试,也是保证代码质量的一种方式. junit单元测试 相信绝大多数的J ...
- 【快学springboot】在springboot中写单元测试
前言 很多公司都有写单元测试的硬性要求,在提交代码的时候,如果单测通不过或者说单元测试各种覆盖率不达标,会被拒绝合并代码.写单元测试,也是保证代码质量的一种方式. junit单元测试 相信绝大多数的J ...
- 在Nodejs中贯彻单元测试
在团队合作中,你写好了一个函数,供队友使用,跑去跟你的队友说,你传个A值进去,他就会返回B结果了.过了一会,你队友跑过来说,我传个A值却返回C结果,怎么回事?你丫的有没有测试过啊? 大家一起写个项目, ...
- TDD中的单元测试写多少才够?
测试驱动开发(TDD)已经是耳熟能详的名词,既然是测试驱动,那么测试用例代码就要写在开发代码的前面.但是如何写测试用例?写多少测试用例才够?我想大家在实际的操作过程都会产生这样的疑问. 3月15日,我 ...
- Autofac - MVC/WebApi中的应用
Autofac前面写了那么多篇, 其实就是为了今天这一篇, Autofac在MVC和WebApi中的应用. 一.目录结构 先看一下我的目录结构吧, 搭了个非常简单的架构, IOC(web), IBLL ...
- 在asp.net WebAPI 中 使用Forms认证和ModelValidata(模型验证)
一.Forms认证 1.在webapi项目中启用Forms认证 Why:为什么要在WebAPI中使用Forms认证?因为其它项目使用的是Forms认证. What:什么是Forms认证?它在WebAP ...
- 在WebAPI中自动创建Controller
在MIS系统中,大部分的操作都是基本的CRUD,并且这样的Controller非常多. 为了复用代码,我们常常写一个泛型的基类. public class EntityController<T& ...
- webApi中参数传递
webApi中参数传递 一:无参数的get方法: 前端: function GetNoParam() { //为了统一:我们都采用$.ajax({}) 方法; $.ajax({ url: '/a ...
- 在Android Studio中进行单元测试和UI测试
本篇教程翻译自Google I/O 2015中关于测试的codelab,掌握科学上网的同学请点击这里阅读:Unit and UI Testing in Android Studio.能力有限,如有翻译 ...
- Visual Studio 中的单元测试 UNIT TEST
原文:Visual Studio 中的单元测试 UNIT TEST 注:本文系作者原创,可随意转载,但请注明出处.如实在不愿注明可留空,强烈反对更改原创出处.TDD(Test-Driven Devel ...
随机推荐
- python + pytestTestreport生成测试报告_报告没有生成图标和报告样式错乱
pytestreport 生成测试报告的问题 1.生成报告html页面的样式错乱 2.生成报告html页面的图标没有展示 3. 生成报告html页面的查询详情按钮点击没有相应 问题排除: 浏览器开发者 ...
- C++判断字符串是否相等
话不多说,上代码 bool str_same(char *p1, char *p2) { if (strcmp(p1, p2)) return 0; else for (int i = 0; i &l ...
- WPF 做一个超级简单的 1024 数字接龙游戏
这是一个我给自己做着玩的游戏,没有什么复杂的界面,就一些简单的逻辑 游戏的规则十分简单,那就是有多个列表.程序会给出一个数字,玩家决定数字放在哪个列表里面.如果放入列表里面的数字和列表里面最后一个数字 ...
- 实验11.ACL实验
# 实验11.ACL实验 本实验用于测试ACL,类似于防火墙. 拓扑 要求阻塞PC1到PC2和server的全部协议,阻塞client1到server1的icmp协议 具体配置 首先利用ospf协议实 ...
- Linux设备模型:6、Bus
作者:wowo 发布于:2014-4-15 19:21 分类:统一设备模型 原创文章,转发请注明出处.蜗窝科技,www.wowotech.net. 概述 在Linux设备模型中,Bus(总线)是一类特 ...
- Coap 协议学习:1-有关概念
COAP协议简介 不像人接入互联网的简单方便,由于物联网设备大多都是资源限制型的,有限的CPU.RAM.Flash.网络宽带等.对于这类设备来说,想要直接使用现有网络的TCP和HTTP来实现设备实现信 ...
- Coding:小写一个debugfs
Coding:小写一个debugfs 上一次整活还是在上一个月,写了一个简单的module并且熟悉了module的挂载查看和卸载.这一次我们自然玩一个大的,就是利用linux的debugfs AP ...
- VulnHub_DC-4渗透流程
VulnHub_DC-4 DC-4 is another purposely built vulnerable lab with the intent of gaining experience in ...
- oeasy教您玩转vim - 42 - # 剪切进入
剪切进入 回忆上节课内容 上次我们了解到了各种寄存器 :reg 无名寄存器"" 数字寄存器"0-"9 行内删除专用寄存器"- 指定寄存器" ...
- [oeasy]python0014_二进制_binary_bin
二进制(binary) 回忆上次内容 上次我们了解了ASCII码表 ASCII 码表就是 American Standard Code for Inf ...