本文须要您了解ASP.NET Core MVC/Web API, xUnit以及Moq相关知识.html
这里有xUnit和Moq的介绍: http://www.javashuo.com/article/p-rqwpmfqu-gm.htmlapp
Controllers能够说是ASP.NET Core MVC/Web API项目的核心, 它们把整个应用都整合到了一块儿. 能够说Controllers是很是重要的, 因此咱们应该对它们作一些测试.async
因为我几乎只作API, 因此本文不包括关于MVC功能的测试, 只包括Controller的API相关功能.函数
先举一个简单点的例子:单元测试
这个Controller相对简单, 它有一个依赖项.测试
它一个方法, 返回类型是IActionResult, 又具体分为两种状况.ui
首先须要new出来一个被测试的RootController, 标准的叫法叫System Under Test(被测试系统). 它须要一个urlHelper做为依赖项, 那就Mock一个便可.url
每组测试数据都会走一遍构造函数的.spa
该测试方法使用的是Theory, 用了4组数据. 执行方法后返回的结果类型应该实现了IActionResult接口, 这里能够用Assert.IsAssignableFrom<TExpected>(actual)来判断.3d
注: 为了方便, 我使用了resharper.
测试以前必定要从新Build一下.
而后再点击resharper在方法旁边提供的测试按钮便可:
从图能够看出resharper提供了方便快捷的图标, 在这你能够选择运行或者调试测试.
测试会经过的, Theory下属的4组数据将被视为4个单独的测试:
我又添加了两个测试方法, 来测试该方法的不一样路径及返回结果:
一般一个测试方法里应该只有一个Assert. 可是第二方法里面有两个Assert, 这是由于这两个Assert都是测试的同一个行为, 因此我认为这样应该是能够的.
Rebuild, 测试:
也是OK的.
看起来针对RootController的GetRoot()方法, 咱们好像已经测试了全部可执行的路径. 让咱们使用测试代码覆盖率这个功能来肯定一下.
点击resharper在测试类旁边提供的CoverAll按钮:
随后会出现单元测试窗口和覆盖率窗口.
直接看覆盖率窗口:
能够看到该Controller和方法的覆盖率都是100%了.
来到被测试的RootController里:
Resharper(其实是dotCover) 在代码的左边显示出了该行代码是否已经被测试覆盖, 若是都是绿色的就说明都被覆盖了.
Resharper的代码覆盖率结果能够导出多种格式:
例如导出HTML后也能够查看覆盖率明细:
这个ProductController略微复杂一点, 首先它须要不少依赖项.
看它的POST Action方法, 不少地方须要被测试:
首先能够测试product为null的状况, 可是这个太简单了, 我就不啰嗦了.
那就测试ModelState.Invalid状况吧:
为了让ModelState Invalid, 我手动添加了ModelState的error. 和被测试方法其它必要的参数.
该方法有三个Assert, 首先断定结果类型是否为UnprocessableEntityObjectResult(422状态码), 而后再断定返回结果包含了ModelState的error.
该测试会pass, 并会覆盖这部分相关的代码:
这里须要使用moq了, 为了让被测试方法顺利跑完, 我设定Mock版的UnitOfWork的SaveAsync()方法会返回true, (注意这个方法的返回类型是Task<bool>):
而后经过moq的Verify()方法断定repository的AddProduct()和unitOfWork的SaveAsync()方法分别被调用了.
Build, 测试会pass, 覆盖率目前比较大了(可是覆盖率100%并不能说明代码没问题):
该项目使用的是EFCore, 在_unitOfWorkSaveAsync()以后, 变量productModel的Id就会有非0值了, 也就是说productModel在_unitOfWorkSaveAsync()方法执行以后发生了变化.
针对这种状况, 咱们可使用moq的Callback()功能:
刚开始为autoMapper的两次map动做设定了返回值.
而后在UnitOfWork的SaveAsync()执行后有个Callback()回调, 回调时至关于模拟了EFCore的保存, 把最新的值赋给了productModel(看被测试代码), (其实这里不用Callback也行....).
随后就是一系列的Assert, 断定某些方法是否执行, 返回类型是否正确, 返回的数据是否正确等.
Build 测试会经过的:
目前该方法还有两处地方没有被覆盖:
能够再写两个测试来覆盖它们:
这两个很简单, 很少介绍了, 注意这里使用了async版本的Assert.Throws().
这两个测试会pass, 最终该方法的代码覆盖率就达到100%了:
ASP.NET Core Web API Controller的测试就介绍这些吧.