本篇将结合这个系列的例子的基础上演示在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 ...
随机推荐
-
门面模式的典型应用 Socket 和 Http(post,get)、TCP/IP 协议的关系总结
门面模式的一个典型应用:Socket 套接字(Socket)是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元.它是网络通信过程中端点的抽象表示,包含进行网络通信必须的五种信息: 连接使用的 ...
-
Windows 10通过本地镜像离线安装.NET 3.5
在Windows10中,当我们安装某些软件的时候会提示"你的电脑上的应用需要使用以下Windows功能:.NET Framework 3.5(包括.NET 2.0和3.0)",由于 ...
-
android学习日记22--Animation动画简介
Animation动画主要有两种:帧动画(Frame Animation)和补间动画(Tween Animation).补间动画主要包括对位置.角度.尺寸等属性的变化,而帧动画则是通过若干帧图片轮流切 ...
-
Installshield: custom action return value
参考:MSDN: Custom Action Return Values 参考:MSDN: Logging of Action Return Values
-
Python之路,Day10 - 异步IO\数据库\队列\缓存
Python之路,Day9 - 异步IO\数据库\队列\缓存 本节内容 Gevent协程 Select\Poll\Epoll异步IO与事件驱动 Python连接Mysql数据库操作 RabbitM ...
-
采用sharedPreference保存数据
1.sharedPreference保存数据 package com.example.login.service; import java.io.BufferedReader; import java ...
-
Dubbo与Zookeeper、SpringMVC整合和使用(负载均衡、容错)(转)
互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,Dubbo是一个分布式服务框架,在这种情况下诞生的.现在核心业务抽取出来,作为独立的服务,使 ...
-
SpringMVC 上下文webApplicationContext
使用listener听众载入配置,一般Struts+Spring+Hibernate是使用listener监听器的.例如以下 <listener> <listener-class&g ...
-
微信公众平台网页登录授权多次重定向跳转,导致code使用多次问题
背景:微信网站开发 昨天我负责的一个项目忽然出现了一个十分诡异的bug,进行微信授权登录的时候请求code的时候安卓手机会多次重定向调转我的接口接收code的接口(redirect_uri 微信请求调 ...
-
大神教你如何解决Linux系统80端口被占用
有Linux在centos下面安装webmail服务遇到80端口被占用的问题,导致无法继续安装,下面详细介绍下Linux如何查看.查找.关闭监听80端口服务以更好的的解决80端口被占用的问题. 一.查 ...