使用Visual Studio Code开发Asp.Net Core WebApi学习笔记(九)-- 单元测试
本篇将结合这个系列的例子的基础上演示在Asp.Net Core里如何使用XUnit结合Moq进行单元测试,同时对整个项目进行集成测试。
第一部分、XUnit
修改 Project.json 文件内容,增加XUnit相关的nuget包引用,并修改部分配置。
{
"version": "1.0.0-*",
"testRunner": "xunit", // 设置测试工具为xunit
"buildOptions": {
"debugType": "portable",
"emitEntryPoint": true
},
"dependencies": {
"Microsoft.NETCore.App": {
"type": "platform",
"version": "1.0.0"
},
"Microsoft.AspNetCore.Server.Kestrel": "1.0.0",
"Microsoft.AspNetCore.Mvc": "1.0.0",
"Microsoft.Extensions.Logging": "1.0.0",
"Microsoft.Extensions.Logging.Console": "1.0.0",
"Microsoft.Extensions.Logging.Debug": "1.0.0",
"Microsoft.Extensions.Logging.Filter": "1.0.0",
"NLog.Extensions.Logging": "1.0.0-rtm-alpha2",
"Autofac.Extensions.DependencyInjection": "4.0.0-rc3-309",
"Microsoft.Extensions.Configuration": "1.0.0",
"Microsoft.Extensions.Configuration.FileExtensions": "1.0.0",
"Microsoft.Extensions.Configuration.Json": "1.0.0",
"Microsoft.Extensions.Options.ConfigurationExtensions": "1.0.0",
"xunit": "2.2.0-beta2-build3300",
"dotnet-test-xunit": "2.2.0-preview2-build1029"
},
"frameworks": {
"netcoreapp1.0": {
// 设置兼容框架
"imports": [
"dotnet54",
"portable-net45+win8"
]
}
}
}
增加一个Demo类和一个测试类
namespace WebApiFrame
{
public class DemoModel
{
public int Add(int a, int b)
{
return a + b;
} public bool IsOdd(int num)
{
return num % == ;
}
}
}
using Xunit; namespace WebApiFrame.Test
{
public class DemoModelTest
{
private readonly DemoModel _demo; public DemoModelTest()
{
_demo = new DemoModel();
} [Fact]
public void AddTest()
{
int result = _demo.Add(, );
Assert.Equal(, result);
}
}
}
打开cmd窗口,进入到项目根目录,输入命令 dotnet test ,将启动单元测试,可以在输出查看测试结果

再对另外一个方法添加单元测试代码
[Theory]
[InlineData()]
[InlineData()]
[InlineData()]
public void IsOdd(int num)
{
bool result = _demo.IsOdd(num);
Assert.True(result, $"{num} is not odd.");
}
再次启动单元测试,查看测试结果

结果显示执行了四个单元测试用例,有一个失败了。
通过比较上面两个测试方法可以发现使用的特性标识不同,测试方法的参数列表也不相同。
[Face]特性标识表示固定输入的测试用例,而[Theory]特性标识表示可以指定多个输入的测试用例,结合InlineData特性标识使用。在上面的例子里,总共使用了三次InlineData特性标识,每次设定的值都不同,在执行单元测试时,设定的值会被测试框架赋值到对应的测试方法的参数里。
第二部分、Moq
在之前的例子里已经定义了如下接口和类
using System.Collections.Generic;
using WebApiFrame.Models; namespace WebApiFrame.Repositories
{
public interface IUserRepository
{
IEnumerable<User> GetAll(); User GetById(int id);
}
}
IUserRepository.cs
using System;
using Microsoft.AspNetCore.Mvc;
using WebApiFrame.Models;
using WebApiFrame.Repositories; namespace WebApiFrame.Controllers
{
[Route("api/[controller]")]
public class UsersController : Controller
{
private readonly IUserRepository userRepository; public UsersController(IUserRepository userRepo)
{
userRepository = userRepo;
} [HttpGet]
public IActionResult GetAll()
{
var list = userRepository.GetAll();
return new ObjectResult(list);
} [HttpGet("{id}")]
public IActionResult Get(int id)
{
var user = userRepository.GetById(id);
return new ObjectResult(user);
} #region 其他方法
// ......
#endregion
}
}
UsersController.cs
我们要对 UsersController.cs 的方法进行单元测试,同时UserRepository实例是通过构造函数依赖注入的,所以要借助Moq来模拟这个实例的生成。
在引入Moq包之前,先要修改NuGet.Config配置文件,增加package包源地址。
NuGet.Config配置文件路径: C:\Users\{user}\AppData\Roaming\NuGet
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<activePackageSource>
<add key="nuget.org" value="https://www.nuget.org/api/v2/" />
</activePackageSource>
<packageSources>
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" /> <!-- 增加的程序包源地址 -->
<add key="aspnet-contrib" value="https://www.myget.org/F/aspnet-contrib/api/v3/index.json" />
</packageSources>
</configuration>
引入Moq相关nuget包: "moq.netcore": "4.4.0-beta8"
添加单元测试类
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Moq;
using WebApiFrame.Controllers;
using WebApiFrame.Models;
using WebApiFrame.Repositories;
using Xunit; namespace WebApiFrame.Test
{
public class UsersControllerTest
{
private readonly UsersController _controller; public UsersControllerTest()
{
var mockRepo = new Mock<IUserRepository>();
mockRepo.Setup(repo => repo.GetAll()).Returns(GetUsers());
_controller = new UsersController(mockRepo.Object);
} [Fact]
public void GetAllTest()
{
IActionResult actionResult = _controller.GetAll();
var objectResult = Assert.IsType<ObjectResult>(actionResult);
var result = Assert.IsAssignableFrom<IEnumerable<User>>(objectResult.Value);
Assert.Equal(, result.Count());
} private IEnumerable<User> GetUsers()
{
return new List<User>()
{
new User(){ Id = , Name = "name:1", Sex = "Male" },
new User(){ Id = , Name = "name:2", Sex = "Female" },
new User(){ Id = , Name = "name:3", Sex = "Male" },
};
}
}
}
在cmd窗口执行单元测试,查看测试结果

在一个分层结构清晰的项目里,各层之间依赖于事先约定好的接口。在多人协作开发时,大多数人都只会负责自己的那一部分模块功能,开发进度通常情况下也不一致。当某个开发人员需要对自己的模块进行单元测试而依赖的其他模块还没有开发完成时,则需要对依赖的接口通过Mock的方式提供模拟功能,从而达到在不实际依赖其他模块的具体功能的情况下完成自己模块的单元测试工作。
第三部分、集成测试
以上的例子只是对逻辑进行了单元测试。对于Asp.Net Core项目,还需要模拟在网站部署的情况下对各个请求入口进行测试。通常情况下可以借助Fiddler等工具完成,在.Net Core里也可以用编程的方式完成测试。
首先引入测试需要的nuget包。因为我们测试的是WebApi接口,响应内容都是json格式的字符串,所以还需要引用json序列化的nuget包。
"Microsoft.AspNetCore.TestHost": "1.0.0",
"Newtonsoft.Json": "9.0.1"
添加测试类
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using Newtonsoft.Json;
using WebApiFrame.Models;
using Xunit; namespace WebApiFrame.Test
{
public class WebApiFrameTest
{
private readonly TestServer _server;
private readonly HttpClient _client; public WebApiFrameTest()
{
_server = new TestServer(new WebHostBuilder().UseStartup<Startup>());
_client = _server.CreateClient();
} [Fact]
public async Task GetAllTest()
{
var response = await _client.GetAsync("/api/users");
response.EnsureSuccessStatusCode(); var responseString = await response.Content.ReadAsStringAsync();
IList<User> users = JsonConvert.DeserializeObject<IList<User>>(responseString); Assert.Equal(, users.Count);
} [Theory]
[InlineData()]
[InlineData()]
[InlineData()]
public async Task GetTest(int id)
{
var response = await _client.GetAsync($"/api/users/{id}");
response.EnsureSuccessStatusCode(); var responseString = await response.Content.ReadAsStringAsync();
User user = JsonConvert.DeserializeObject<User>(responseString); Assert.NotNull(user);
}
}
}
在cmd窗口执行单元测试,查看测试结果

在上面的例子里,通过在一个工程里同时模拟了服务端(TestServer)和客户端(HttpClient)的通信,从而达到了整体测试WebApi接口的目的。
使用Visual Studio Code开发Asp.Net Core WebApi学习笔记(九)-- 单元测试的更多相关文章
- 使用Visual Studio Code开发Asp.Net Core WebApi学习笔记(一)-- 起步
本文记录了在Windows环境下安装Visual Studio Code开发工具..Net Core 1.0 SDK和开发一个简单的Web-Demo网站的全过程. 一.安装Visual Studio ...
- 使用Visual Studio Code开发Asp.Net Core WebApi学习笔记(八)-- 多环境开发
本篇将演示Asp.Net Core如何在多环境下进行开发适配. 在一个正规的开发流程里,软件开发部署将要经过三个阶段:开发.测试.上线,对应了三个环境:开发.测试.生产.在不同的环境里,需要编写不同的 ...
- 使用Visual Studio Code开发Asp.Net Core WebApi学习笔记(十)-- 发布(Windows)
本篇将在这个系列演示的例子上继续记录Asp.Net Core在Windows上发布的过程. Asp.Net Core在Windows上可以采用两种运行方式.一种是自托管运行,另一种是发布到IIS托管运 ...
- 使用Visual Studio Code开发Asp.Net Core WebApi学习笔记(三)-- Logger
本篇是在上一篇的基础上添加日志功能,并记录NLog在Asp.Net Core里的使用方法. 第一部分:默认Logger支持 一.project.json添加日志包引用,并在cmd窗口使用 dotnet ...
- [转]使用Visual Studio Code开发Asp.Net Core WebApi学习笔记(三)-- Logger
本文转自:https://www.cnblogs.com/niklai/p/5662094.html 本篇是在上一篇的基础上添加日志功能,并记录NLog在Asp.Net Core里的使用方法. 第一部 ...
- 使用Visual Studio Code开发Asp.Net Core WebApi学习笔记(七)-- 结构化配置
本篇将记录.Net Core里颇有特色的结构化配置的使用方法. 相比较之前通过Web.Config或者App.Config配置文件里使用xml节点定义配置内容的方式,.Net Core在配置系统上发生 ...
- 使用Visual Studio Code开发Asp.Net Core WebApi学习笔记(六)-- 依赖注入
本篇将介绍Asp.Net Core中一个非常重要的特性:依赖注入,并展示其简单用法. 第一部分.概念介绍 Dependency Injection:又称依赖注入,简称DI.在以前的开发方式中,层与层之 ...
- 使用Visual Studio Code开发Asp.Net Core WebApi学习笔记(五)-- Filter
在上一篇里,介绍了中间件的相关内容和使用方法.本篇将介绍Asp.Net Core MVC框架的过滤器的相关内容和使用方法,并简单说明一下与中间件的区别. 第一部分.MVC框架内置过滤器 下图展示了As ...
- 使用Visual Studio Code开发Asp.Net Core WebApi学习笔记(四)-- Middleware
本文记录了Asp.Net管道模型和Asp.Net Core的Middleware模型的对比,并在上一篇的基础上增加Middleware功能支持. 在演示Middleware功能之前,先要了解一下Asp ...
随机推荐
- [UIView beginAnimations:context:]与[UIView animateWithDuration:animations:]值得注意的一个区别
原文链接:http://longtimenoc.com/archives/uiview-beginanimationscontext%E4%B8%8Euiview-animatewithduratio ...
- js中使用s(c)标签
在js或者jquery中使用s标签,其实并不难理解,s标签也只是一个标签而已,当你想象成js+s标签=js+html标签就理解了 例如: <script type="text/java ...
- 算法库:blas, lapack, cblas, clapack, armadillo, openblas, mkl关系
关于blas的介绍介绍见:http://www.cnblogs.com/dzyBK/p/4983953.html blas:提供向量和矩阵的基本运算,用fortran编写. lapack:提供向量和矩 ...
- [datatable]C# DataTable 如何排序
DataTable dt = new DataTable(); dt.Columns.Add("ProductID", typeof(string)); dt.Columns.Ad ...
- java左移右移运算符
http://blog.csdn.net/dandanteng/article/details/7433531 首先要明白一点,这里面所有的操作都是针对存储在计算机中中二进制的操作,那么就要知道,正数 ...
- Android性能优化之:ViewStub
ViewStub是Android布局优化中一个很不错的标签/控件,直接继承自View.虽然Android开发人员基本上都听说过,但是真正用的可能不多. ViewStub可以理解成一个非常轻量级的Vie ...
- 【转】appium_python_API文档
1.contextscontexts(self): Returns the contexts within the current session. 返回当前会话中的上下文,使用后可以识别H5页面的控 ...
- 慧自文档:代替 Everything 来快速查找文件的,实现文件显示在文件夹的层次结构中
1. 搜索功能和Everything一样快和强大 具有 Everything 搜索快.搜索功能强等优点, 解决了不能方便选择搜索哪个文件夹, 解决了不能同一个画面进行预览等问题 2.文件直接显示到文件 ...
- &&和||的那点事儿
以前一直以为&&和||的运算结果就是布尔值,但今天看到一段代码又填补的一些知识漏洞. var a = (1&&2&&5) || 3; console.l ...
- Linux-wget/tar/ln 函数
1. 获取软件包,可以使用wget的方式, ubuntu可以使用apt-get source来获取源代码 wget 是一个在网络上进行下载的简单而强大的自由软件,支持HTTP,HTTPS,FTP协议, ...