本篇将结合这个系列的例子的基础上演示在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学习笔记(九)-- 单元测试的更多相关文章

  1. 使用Visual Studio Code开发Asp.Net Core WebApi学习笔记(一)-- 起步

    本文记录了在Windows环境下安装Visual Studio Code开发工具..Net Core 1.0 SDK和开发一个简单的Web-Demo网站的全过程. 一.安装Visual Studio ...

  2. 使用Visual Studio Code开发Asp.Net Core WebApi学习笔记(八)-- 多环境开发

    本篇将演示Asp.Net Core如何在多环境下进行开发适配. 在一个正规的开发流程里,软件开发部署将要经过三个阶段:开发.测试.上线,对应了三个环境:开发.测试.生产.在不同的环境里,需要编写不同的 ...

  3. 使用Visual Studio Code开发Asp.Net Core WebApi学习笔记(十)-- 发布(Windows)

    本篇将在这个系列演示的例子上继续记录Asp.Net Core在Windows上发布的过程. Asp.Net Core在Windows上可以采用两种运行方式.一种是自托管运行,另一种是发布到IIS托管运 ...

  4. 使用Visual Studio Code开发Asp.Net Core WebApi学习笔记(三)-- Logger

    本篇是在上一篇的基础上添加日志功能,并记录NLog在Asp.Net Core里的使用方法. 第一部分:默认Logger支持 一.project.json添加日志包引用,并在cmd窗口使用 dotnet ...

  5. [转]使用Visual Studio Code开发Asp.Net Core WebApi学习笔记(三)-- Logger

    本文转自:https://www.cnblogs.com/niklai/p/5662094.html 本篇是在上一篇的基础上添加日志功能,并记录NLog在Asp.Net Core里的使用方法. 第一部 ...

  6. 使用Visual Studio Code开发Asp.Net Core WebApi学习笔记(七)-- 结构化配置

    本篇将记录.Net Core里颇有特色的结构化配置的使用方法. 相比较之前通过Web.Config或者App.Config配置文件里使用xml节点定义配置内容的方式,.Net Core在配置系统上发生 ...

  7. 使用Visual Studio Code开发Asp.Net Core WebApi学习笔记(六)-- 依赖注入

    本篇将介绍Asp.Net Core中一个非常重要的特性:依赖注入,并展示其简单用法. 第一部分.概念介绍 Dependency Injection:又称依赖注入,简称DI.在以前的开发方式中,层与层之 ...

  8. 使用Visual Studio Code开发Asp.Net Core WebApi学习笔记(五)-- Filter

    在上一篇里,介绍了中间件的相关内容和使用方法.本篇将介绍Asp.Net Core MVC框架的过滤器的相关内容和使用方法,并简单说明一下与中间件的区别. 第一部分.MVC框架内置过滤器 下图展示了As ...

  9. 使用Visual Studio Code开发Asp.Net Core WebApi学习笔记(四)-- Middleware

    本文记录了Asp.Net管道模型和Asp.Net Core的Middleware模型的对比,并在上一篇的基础上增加Middleware功能支持. 在演示Middleware功能之前,先要了解一下Asp ...

随机推荐

  1. c#程序中使用"like“查询access数据库语句的问题

    在写使用access数据库的c#程序过程中,遇到各种莫名奇妙的问题.例如使用"like"进行模糊查询,在access查询视图中要使用"*"做模糊匹配(sql中是 ...

  2. Java基础-事件处理

  3. 自己动手搭建 Redis 环境,并建立一个 .NET HelloWorld 程序测试(转)

    关于 Redis ,下面来自百度百科: redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set( ...

  4. [Flex] Accordion系列 - Header图标的设置

    <?xml version="1.0" encoding="utf-8"?> <!--Flex中如何通过getHeaderAt()函数以及se ...

  5. 页面设计--RadioButton

    RadioButton单选控件支持多分组模式 属性如下图 设计: web显示效果图:

  6. Temporary Segments: What Happens When a Sort Occurs (文档 ID 102339.1)

    APPLIES TO: Oracle Database - Enterprise Edition - Version 8.1.7.4 to 11.2.0.1 [Release 8.1.7 to 11. ...

  7. 在iis中注册.net framework

    首先定位到文件夹:cd C:\Windows\Microsoft.NET\Framework\v4.0.30319 执行命令:aspnet_regiis.exe -i

  8. 腾讯校招技术岗面试经历及总结(已发offer)

    关于笔试:只要前期复习到位,笔试还是很好过的,但是当然 分数 越高越好,否则后面会被面试官鄙视的.题目可能难度较大,但是要把会做的 都做 对,如果时间比较紧可以适度放弃部分不会的题目. 关于面试: 温 ...

  9. Start:at cnblogs firstDay

    C#旨在设计成为一种"简单.现代.通用",以及面向对象的程序设计语言,此种语言的实现,应提供对于以下软件工程要素的支持:强类型检查.数组维度检查.未初始化的变量引用检测.自动垃圾收 ...

  10. 防范ARP网关欺骗, ip mac双向绑定脚本

    客户局域网内的一台数据库服务器, 重新安装操作系统后,不能上网了,ping网关192.168.0.1出现在800多ms的响应时间,还会超时丢包,检查了ip,路由配置,都没有问题.通过IE打开路由器管理 ...