.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 ...
随机推荐
- time模块:时间戳和格式化好的时间表示方法及互相转换方法
1.导入time模块 import time 2.获取当前时间的时间戳 time.time() 3.获取当前格式化好的时间 time.strftime(想要获取的格式) 4.时间戳和格式化 ...
- ctfhub sql注入字符型
手工注入 1, 检查是否存在注入 2.猜字段数.列数 3.获得注入点,数据库名称,数据库版本 4.获得表名 5.获得字段名 6.获得flag sqlmap方法 1.查数据库库名 2.查表名 3.查字段 ...
- 【二叉树-最长路径系列(任意路径)】直径、最长同值路径、 最大路径和(DFS、树形DP)
总述 这类题目都是求一个最长路径,这个路径可以不经过根节点. 使用dfs(即递归地遍历树)的方法.维护一个全局最长路径max作为最终结果,而递归方法dfs返回的是含根节点的最长路径.(若不使用全局变量 ...
- oracle使用impdp和expdp导入导出数据
1. 导出数据 开始导出数据前,要创建一个directory,因为导入时需要指定directory,导出的dump文件和日志会保存在该directory对应的目录下 SQL> create di ...
- Jmeter引用外部jar包的几种方法
总结记录下jmeter引用外部jar包的3种方法及其优缺点: 一.测试计划中添加目录或jar包到Classpath 操作:测试计划->添加目录或jar包到Classpath-->浏览导入j ...
- linux学习(三)Linux 系统目录结构
一.查看目录 登录系统后,在当前命令窗口下输入命令: ls / 树状目录结构: 二.目录解析 /bin: 存放二进制可执行文件(ls,cat,mkdir等). /boot: 存放启动Linux时使用的 ...
- 如何使用 C# 中的 ValueTask
在 C# 中利用 ValueTask 避免从异步方法返回 Task 对象时分配 翻译自 Joydip Kanjilal 2020年7月6日 的文章 <How to use ValueTask i ...
- Hive改表结构的两个坑|避坑指南
Hive在大数据中可能是数据工程师使用的最多的组件,常见的数据仓库一般都是基于Hive搭建的,在使用Hive时候,遇到了两个奇怪的现象,今天给大家聊一下,以后遇到此类问题知道如何避坑! 坑一:改变字段 ...
- iOS14 debug安装的带有flutter应用从桌面图标重新启动时闪退
刚刚升级遇到的问题,希望能够帮助到遇到相同问题的人. 用xcode12连接iOS设备调试安装应用 拔掉数据线 从桌面图标点击进入App必闪退 后来发现是flutter的原因,目前有下面两种个解决方案: ...
- Python练习题 021:递归方法求阶乘
[Python练习题 021] 利用递归方法求5!. ---------------------------------------------- 首先得弄清楚:5! 指的是"5的阶乘&qu ...