本篇将结合这个系列的例子的基础上演示在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. [kuangbin带你飞]专题二十二 区间DP

            ID Origin Title   17 / 60 Problem A ZOJ 3537 Cake   54 / 105 Problem B LightOJ 1422 Hallowee ...

  2. [HDU 2126] Buy the souvenirs (动态规划)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2126 题意:给你n个物品,m元钱,问你最多能买个多少物品,并且有多少种解决方案. 一开始想到的是,先解 ...

  3. 11gr2 alert日志中报TNS-12535 TNS-00505原因及解决方法 (转载)

    前面新装了11GR2 RAC,某天在做巡检的时候发现alert日志中存在如下报错:Fatal NI connect error 12170. VERSION INFORMATION:        T ...

  4. javascript里面支持el表达式和<s:iterator>

    javascript不支持jstl标签,支持<s:iterator>和el表达式

  5. AM3359之U-boot及kernel编译

    我用的PC主机是ubuntu12.04 LST 32位系统给出官方软件下载地址:TI官网SDK包06.00版本下载:http://software-dl.ti.com/sitara_linux/esd ...

  6. 寻找最合适的view

    *:first-child { margin-top: 0 !important; } body > *:last-child { margin-bottom: 0 !important; } ...

  7. 在word中做复选框打对勾钩

    在word中做复选框打对勾钩 现在终于搞明白正确的操作方法 一.你在word里输入2610,按alt+X就能出 空checkbox 你在word里输入2611,按alt+X就能出 打了勾的checkb ...

  8. 未完全关闭数据库导致ORA-01012: not logged的解决

    首先使用SHUTDOWN 没加任何参数方式关闭数据库,在数据库未关闭时CTRL+Z停止执行,退出用SQLPLUS重登陆,出现报错:ORA-01012: not logged on SQL> st ...

  9. 【转贴】全站 HTTPS 来了

    http://geek.csdn.net/news/detail/48765 作者:腾讯TEG架构平台部静态加速组高级工程师 刘强 最近大家在使用百度.谷歌或淘宝的时候,是不是注意浏览器左上角已经全部 ...

  10. Android开发-API指南-Fragment

    Fragments 英文原文:http://developer.android.com/guide/components/fragments.html 采集日期:2014-12-31 在本文中 设计理 ...