.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 ...
随机推荐
- Dubbo系列之 (七)链路层那些事(1)
辅助链接 Dubbo系列之 (一)SPI扩展 Dubbo系列之 (二)Registry注册中心-注册(1) Dubbo系列之 (三)Registry注册中心-注册(2) Dubbo系列之 (四)服务订 ...
- 分别用canvas和css3的transform做出钟表的效果
两种方式实际上在js上的原理都是一样的.都是获取时间对象,再获取时间对象的时分秒,时分秒乘以其旋转一刻度(一秒.一分.一小时)对应的角度.css3中要赋值于transform:rotate(角度),c ...
- Mqtt协议 服务器交互
1.服务器发送消息 package demo1; import org.eclipse.paho.client.mqttv3.MqttClient; import org.eclipse.paho.c ...
- k8s架构分析(二)
master节点 k8s的集群由master和node组成,节点上运行着若干k8s服务. master节点之上运行着的后台服务有kube-apiserver .kube-scheduler.kube- ...
- Vue render函数 函数时组件 jsx
常规组件使用 定义组件 components/list/list.vue <template> <ul> <li v-for="(item, index) in ...
- Class对象、反射、动态代理
Class对象是所有类的根源,Object是所有对象的根源. 编译后的新类会产生一个Class对象,保存在同名的.class文件中.每个类都有一个Class对象,它包含了所有的与类有关的信息.所有的C ...
- WEB 应用缓存解析以及使用 Redis 实现分布式缓存
什么是缓存? 缓存就是数据交换的缓冲区,用于临时存储数据(使用频繁的数据).当用户请求数据时,首先在缓存中寻找,如果找到了则直接返回.如果找不到,则去数据库中查找.缓存的本质就是用空间换时间,牺牲数据 ...
- Alink漫谈(二十二) :源码分析之聚类评估
Alink漫谈(二十二) :源码分析之聚类评估 目录 Alink漫谈(二十二) :源码分析之聚类评估 0x00 摘要 0x01 背景概念 1.1 什么是聚类 1.2 聚类分析的方法 1.3 聚类评估 ...
- python-igraph
linux安装python-igraph: $ sudo apt-get install -y libigraph0-dev $ pip install python-igraph ------for ...
- burp suite之spider(爬虫)
spider (蜘蛛,这里的意思指爬行) 像蜘蛛一样在网站上爬行出网站的个个目录信息,并发送至Target. 1.Control(控制) Spider is paused :停止蜘蛛爬行 Clear ...