.Net Core单元测试规范
.Net Core单元测试规范
一、 前言
为了有效提升代码质量,保证DevOps的顺利进行。将全面开始采用单元测试进行覆盖,届时单元测试将完全纳入
到完整的持续构建生命周期中做为第一道质量把控的门槛。
二、 预期目标
1. 避免直接异常
考虑到单元测试的细化程度,在代码级别上出现的故障将能够通过单元测试进行快速的挖掘。特别对于部分通
用业务代码,在修改一处后其他关联地方一旦没有及时修改将会导致重大事故,而借助于单元可以完全避免这
类情况的发生。
2. 故障产生前置
由于单元测试不依赖外部其他环境,是完全属于代码级别的测试。所以开发者仅仅通过本地也可以进行测
试,保证在相关功能代码编写完成后立即进行相关测试保证代码在提交前就能够挖掘其中的问题。
3. 质量管控可视化
借助于持续构建,过程中所有的单元测试报告通过采集后将可以有效的反应整个项目中各个开发人员的质
量情况,并通过看板及时有效的反应。
三、 应用层单元测试
1. 相关技术
为了能够快速的进行单元测试,需要相关人员具备一定的技术基础,下面将会给出在当前情况下需要使
用的相关单元测试技术以便于开发人员进行基础的学习同时也能够基于官方文档进行更深入的学习以提
升对于对于复杂情况下的应对能力。
Moq
考虑到当前架构的复杂性,其中大量采用了IOC技术,为了能够实现单元测试需要借助于相关的Mock技
术对底层或者第三方服务接口的模拟以实现各类环境的验证作用。xUnit
核心单元测试框架,其提供了简单的操作方式。同时也能够支持dotnet指令直接进行相关测试的论证。
2. 仓储模拟
应用层服务都需要依赖外部的各类存储,为了避免对于外部环境的依赖。对于单元测试来说需要将需
要测试的应用服务依赖的仓储接口进行模拟,以便进行具体的测试工作,这里以如下的仓储接口为例:
/// <summary>
/// 产品仓储接口
/// </summary>
public interface IProductInfoRepositories : IRepository<ProductInfo, string>, ITransientDependency
{
/// <summary>
/// 查询产品详情
/// </summary>
/// <param name="id">主键</param>
/// <returns></returns>
Task<ProductInfo> GetProductInfoAsync(string id);
/// <summary>
/// 查询产品列表
/// </summary>
/// <returns></returns>
Task<List<ProductInfo>> GetProductInfoListAsync();
/// <summary>
/// 修改产品包年折扣
/// </summary>
/// <param name="id">主键</param>
/// <param name="discount">折扣</param>
/// <returns></returns>
Task UpdateProductInfoDiscountAsync(string id, decimal discount);
/// <summary>
/// 根据序号查询产品信息
/// </summary>
/// <param name="number">产品序号</param>
/// <returns></returns>
Task<ProductInfo> GetProductInfoByNumberAsync(string number);
}
确定我们需要进行模拟的仓储接口后,我们需要在对应的测试工程项目下新建Mocks文件
夹并在其中新建文件ProductInfoRepositoryMock类,其中我们需要使用moq进行仓储
接口的模拟,比如对其中的GetProductInfoAsync接口进行模拟:
mock.Setup(x => x.GetProductInfoAsync("01")).Returns(Task.FromResult(new ProductInfo
{
Id = "01",
Number = "2019061800001",
Name = "TMS全流程物流运输管理系统",
ImgUrl = "https://avatars2.githubusercontent.com/u/16951448?s=200&v=4",
Discount = 1
}));
mock.Setup(x => x.GetProductInfoAsync("05")).Returns(Task.FromResult(new ProductInfo
{
Id = "05",
Number = "201906180005",
Name = "PMS供应商自主系统",
ImgUrl = "https://avatars2.githubusercontent.com/u/16951448?s=200&v=4",
Discount = (decimal)0.75
}));
mock.Setup(x => x.GetProductInfoAsync("error")).Returns(Task.FromResult<ProductInfo>(null));
利用Setup方法选择需要进行模拟的接口x => x.GetProductInfoAsync("01"),而其中可以选定特定的参数也可
以通过It.IsAny()方法传入参数,表示任意参数调用该方法均返回相同返回值,否则可以根据入参的不
同从而决定不同的出参。对于出参则通过Returns方法中直接放置需要返回的数据。
3. 应用接口测试
完成以上仓储接口的模拟后,对于ProductInfoService来说需要依赖的外部环境都具备了,这时就可以将该对
象进行实例化:
var productInfoRepositoryMock = new ProductInfoRepositoryMock();
_productInfoService = new ProductInfoService(productInfoRepositoryMock.Create())
完成对象的创建后就可以开始编写具体的单元测试代码了,比如针对上述仓储对接口GetProductInfoAsync进
行了模拟,对应的我们则只能针对应用层接口中的GetProductInfoAsync进行测试,首先进行基本的常规测试:
[Theory]
[InlineData("01")]
[InlineData("05")]
public async Task GetProductInfoAsync_IsNormal(string id)
{
var item = await _productInfoService.GetProductInfoAsync(id);
Assert.NotNull(item);
Assert.Equal(id, item.Id);
Assert.NotEmpty(item.Name);
Assert.NotEmpty(item.Number);
Assert.NotEmpty(item.ImgUrl);
}
从上述代码中可以看到其中存在Theory、InlineData这几个注解属性的使用,这类注解属性用于提供不同参
数情况下的单元测试的论证。单元测试往往不是仅考虑正常业务情况下对代码逻辑的测试,更多的还是对于异
常情况下业务代码的正常工作,所以以下还需要对于不存在该数据的情况进行测试。
[Fact]
public async Task GetProductInfoAsync_IsNull()
{
var error = await _productInfoService.GetProductInfoAsync("error");
Assert.Null(error);
}
4. 持续构建
当前我们的整体流水线采用Drone进行管理,所以需要针对.drone.yml增加相关流程节点以支持单元测试
的运行,具体需要增加的配置如下:
- name: UnitTest
image: harbor.vip56.cn/common/sonar:2.2
commands:
- cd test/LogisticsWebsiteUnitTest
- dotnet test --logger:"trx;LogFileName=unitTestLog.txt"
when:
event:
- pull_request
- push
brancch:
- dev
5. 报告反馈
通过上述持续构建中logger参数可以得知具体的单元测试结果将输出到当前目录下的TestResults
文件夹下,其中大致内容如下:
<UnitTest name="LogisticsWebsiteUnitTest.ProductInfoServiceUnitTest.GetProductInfoAsync_IsNormal(id: "01")" storage="g:\gitlab\logisticswebsiteback\test\logisticswebsiteunittest\bin\debug\netcoreapp2.0\logisticswebsiteunittest.dll" id="e950b619-318c-720a-d6ae-08d1f3ab96ec">
<Execution id="61675f18-f972-4f59-901f-68b9f75680de" />
<TestMethod codeBase="G:\gitlab\logisticswebsiteback\test\LogisticswebsiteUnitTest\bin\Debug\netcoreapp2.0\LogisticsWebsiteUnitTest.dll" adapterTypeName="executor://xunit/VsTestRunner2/netcoreapp" className="LogisticsWebsiteUnitTest.ProductInfoServiceUnitTest" name="LogisticsWebsiteUnitTest.ProductInfoServiceUnitTest.GetProductInfoAsync_IsNormal(id: "01")" />
</UnitTest>
四、 表现层单元测试
敬请等待
.Net Core单元测试规范的更多相关文章
- .NET Core 单元测试 MSTest
.NET Core 单元测试 MSTest ,MSTest Framework 已经支持 .NET Core RC2 / ASP.NET Core RC2. 之前都是使用 xUnit.net ,现在 ...
- 单元测试系列:JUnit单元测试规范
更多原创测试技术文章同步更新到微信公众号 :三国测,敬请扫码关注个人的微信号,感谢! 原文链接:http://www.cnblogs.com/zishi/p/6762032.html Junit测试代 ...
- .Net Core 编码规范
.Net Core 编码规范 标签: 未分类 概述 规范制定原则 方便代码的交流和维护. 不影响编码的效率,不与大众习惯冲突. 使代码更美观.阅读更方便. 使代码的逻辑更清晰.更易于理解. 术语定义 ...
- 单元测试系列之三:JUnit单元测试规范
更多原创测试技术文章同步更新到微信公众号 :三国测,敬请扫码关注个人的微信号,感谢! 原文链接:http://www.cnblogs.com/zishi/p/6762032.html Junit测试代 ...
- dotnet core 编程规范
本文实际只是翻译 .NET Core foundational libraries 官方文档的编码风格 在 .NET Core foundational libraries项目使用的编程规范默认就是 ...
- .Net基础——程序集与CIL HttpClient封装方法 .Net Core 编码规范 C#中invoke和beginInvoke的使用 WebServeice 动态代理类
.Net基础——程序集与CIL 1. 程序集和CIL: 程序集是由.NET语言的编译器接受源代码文件产生的输出文件,通常分为 exe和dll两类,其中exe包含Main入口方法可以双击执行,dll ...
- 【转】.NET(C#):浅谈程序集清单资源和RESX资源 关于单元测试的思考--Asp.Net Core单元测试最佳实践 封装自己的dapper lambda扩展-设计篇 编写自己的dapper lambda扩展-使用篇 正确理解CAP定理 Quartz.NET的使用(附源码) 整理自己的.net工具库 GC的前世与今生 Visual Studio Package 插件开发之自动生
[转].NET(C#):浅谈程序集清单资源和RESX资源 目录 程序集清单资源 RESX资源文件 使用ResourceReader和ResourceSet解析二进制资源文件 使用ResourceM ...
- Asp.Net Core 单元测试正确姿势
背景 ASP.NET Core 支持依赖关系注入 (DI) 软件设计模式,并且默认注入了很多服务,具体可以参考 官方文档, 相信只要使用过依赖注入框架的同学,都会对此有不同深入的理解,在此无需赘言. ...
- 关于单元测试的思考--Asp.Net Core单元测试最佳实践
在我们码字过程中,单元测试是必不可少的.但在从业过程中,很多开发者却对单元测试望而却步.有些时候并不是不想写,而是常常会碰到下面这些问题,让开发者放下了码字的脚步: 这个类初始数据太麻烦,你看:new ...
随机推荐
- es创建普通索引以及各种查询
创建索引 创建普通索引: PUT /my_index { "settings": { "index": { "number_of_shards&quo ...
- 云计算openstack共享组件——Memcache 缓存系统(4)
一.缓存系统 一.静态web页面: 1.在静态Web程序中,客户端使用Web浏览器(IE.FireFox等)经过网络(Network)连接到服务器上,使用HTTP协议发起一个请求(Request),告 ...
- java学习(九) —— java中的File文件操作及IO流概述
前言 流是干什么的:为了永久性的保存数据. IO流用来处理设备之间的数据传输(上传和下载文件) java对数据的操作是通过流的方式. java用于操作流的对象都在IO包中. java IO系统的学习, ...
- Vue+Java+Base64实现条码解析
前端部分(Vue + Vant) 引入Vant.使用Vant中的Uploader组件上传文件(支持手机拍照) import Vue from 'vue'; import { Uploader } fr ...
- Java基础一篇过(三)I/O流总结
一.啥是I/O 概念:I/O为输入,输出流的统称,流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象. 本质:本质是数据传输,即数据在两设备间的传输称为流,根据数据传输特性将流抽象为各 ...
- Spring学习(八)AOP详解
文章更新时间:2020/04/06 一.一个例子 在上面的例子中,包租婆的核心业务就是签合同,收房租,那么这就够了,灰色框起来的部分都是重复且边缘的事,交给中介商就好了,这就是 AOP 的一个思想:让 ...
- vue学习03 v-html
vue学习03v-html v-html指令的作用是:设置元素的内部html链接 内容有html 的结构会被解析为标签 v-text指令无论内容是什么,只会解析文本 解析文本使用v-text,需要解析 ...
- Python3.8下载安装步骤及环境变量配置详解
安装地址:https://www.python.org/ 打开python官网网址,点击 Python 3.8.5 3.下载与自己电脑系统相匹配的版本(这里以64为例) 点击下载完成后打开文件运行 点 ...
- 一篇文章搞定 Nginx 反向代理与负载均衡
代理 要想弄明白反向代理,首先要知道啥是正向代理,要搞懂正向代理只需要知道啥是代理即可.代理其实就是一个中介,在不同事物或同一事物内部起到居间联系作用的环节.比如买票黄牛,房屋中介等等. 在互联网中代 ...
- Hibernate4.3 HQL查询
HQL:Hibernate专属语言,可以跨数据库 一.基本查询 1 public void testQuery(){ 2 Session session = HibernateUtils.getSes ...