使用 Moq 测试.NET Core 应用 - Why Moq?
什么是Mock
当对代码进行测试的时候, 我们经常需要用到一些模拟(mock)技术.

绿色的是需要被测试的类, 黄色是它的依赖项, 灰色的无关的类
在一个项目里, 我们经常需要把某一部分程序独立出来以便我们可以对这部分进行测试. 这就要求我们不要考虑项目其余部分的复杂性, 我们只想关注需要被测试的那部分. 这里就需要用到模拟(Mock)技术.
因为, 请仔细看. 我们想要隔离测试的这部分代码对外部有一个或者多个依赖. 所以编写测试代码的时候, 我们需要提供这些依赖. 而针对隔离测试, 并不应该使用生产时用的依赖项, 所以我们使用模拟版本的依赖项, 这些模拟版依赖项只能用于测试时, 它们会使隔离更加容易.

绿色的是需要被测试的类, 黄色是Mock的依赖项
Mock技术带来的优点
使用Mock技术, 可以有如下的优点:
- 提高测试运行速度, 例如可以模拟DB, Web Service等比较慢的服务, 以及算法等.
- 支持并行开发, 例如实际的依赖项还没有完成开发, 或者等待其他团队开发依赖项.
- 提高测试可靠性, 例如有时这个依赖项的bug太多了, 经常由于依赖项的原因导致测试失败, 那么就应该使用mock版本来验证我们自己写的代码.
- 减少开发/测试成本, 有时程序可能依赖一些云服务, 这些服务是按调用次数收费的, 那么就可以使用Mock版本来节省这方面的开资, 当然了最后还是需要使用真正的服务测试才行; 有时候组建依赖项太费劲了, 就用mock版本吧, 省时省力.
- 在有不确定性依赖项的情况下进行测试, 有些依赖项有不确定性, 可能无理由的造成测试失败, 这时候就应该使用mock版本的依赖.
单元测试
Mock技术通常在单元测试中使用, 可以使用xUnit来为.NET Core应用做单元测试, 这里有介绍xUnit的文章: https://www.cnblogs.com/cgzl/p/9178672.html#xunit
那么什么是一个单元?
这个通常是由团队对系统的理解决定, 可以针对一个类, 也可以针对多个类.
单元测试通常具有以下特点:
- 低级别
- 高聚焦
- 执行速度快
- 容易测试所有执行路径上的代码
术语
- Test Double (我认为可以翻译为测试替身), 是所有非真实依赖项的总称.
- Fake, Fake是那种可以正常工作的实现, 尽管可以正常工作, 但是它们不可以用于生产环境, 例如EFCore里的内存数据库提供商.
- Dummy, 有时候, 被测试方法需要一些参数, 但是这些参数实际上并没有用到, 这时就可以创建dummy, 它们的存在只是为了满足调用方法的参数要求.
- Stub, (状态测试). 它可以使用很直接的方式模拟依赖项的行为. 例如我们可以使用Stub把相关数据放到内存里查询而不是查询真实的数据库; 如果某个测试类需要依赖项的某个Property的值, 那么stub就设定这个值就行.
- Mock, (行为/交互测试). 与Stub不同的是, Mock期待的不是返回值, Mock期待的是动作的执行. 它是依赖项的动态包装, 它可以对哪个方法以什么样的顺序被待测试系统(SUT)调用的这个期待行为进行预编程. 也就是说被测试的系统只有按照特定的顺序调用mock依赖项的特定方法, 那么该系统才算测试通过.
还有其它的一些术语就不介绍了, 主要是这四个.
对于Stub 和 Mock ,可以看下面两张图例:


Moq
官网: https://github.com/moq/moq4
Moq框架可以用来创建dummy, stub 和 mock. 在本文里把这三个东西都叫做mock对象吧.
Moq使用一套API来创建stub和mock对象.
准备项目
一个简单的.NET Core控制台项目: https://github.com/solenovex/Moq-Tutorial-Code, 代码是里面的01 before.
该项目非常简单, 是关于球员转会业务, 它目前只有三个类.
TransferApplication, 球员转会申请类:

TransferResult, 转会审批结果枚举:

还有TransferApproval, 转会审批类:
'
当前的逻辑是, 发起球员转会申请后, 进行审批: 如果总费用大于预算, 那么就直接拒绝; 如果总费用不超标, 并且球员小于30岁, 那么就批准; 但如果球员大于30岁, 并且是超级巨星的话, 这将由老板决定.
建立单元测试项目
在解决方案里建立一个xUnit类型的项目:

然后要保证该项目所用到的库都保持最新:

最后别忘了添加对FootballManager项目的引用:

打开Text Explorer, 可以看到里面有一个待测的单元测试:

做一个简单的单元测试
把UnitTest1改成下面这个简单的单元测试:

重新Build后, 可以看到单元测试的名称更新了.
点击Run All, 运行单元测试, 结果成功:

随后再添加一个简单的单元测试:

Build, 后就会出现这个测试:

Run All, 测试也会成功:

添加依赖
这时, 有一些需求的变化, 球员转会审批前, 需要通过体检.
首先在转会申请类里面添加两个球员的属性:

然后添加一个体检的接口:

这两个方法的作用是一样的, 但是调用方法略有不同.
但是此时, 该接口的实现类还没有开发完毕:

在转会审批类里面, 需要添加这个依赖, 使用的是接口:

在单元测试类里面, 我为转会球员添加了这两个属性, 但是审批类会报错, 因为没有加入依赖项:

所以测试的时候需要注入这个依赖项IPhysicalExamination, 但是PhysicalExamination类还没有做完(里面的方法都没有实现), 所以我们无法new出来这个类.
这时, 我们也许可以传null进去?

这时, 项目是不报错了.
跑单元测试, Run All:

测试失败, 抛出NullReferenceException. 而这个异常导致了测试无法正常进行.
所以, 我们需要Moq, 它可以提供一个Mock(模拟)版本的IPhysicalExamination, 并把它传递到审批类的构造函数里.
安装Moq
在单元测试项目添加Moq:

Moq的第一篇先到这.
使用 Moq 测试.NET Core 应用 - Why Moq?的更多相关文章
- 使用 Moq 测试.NET Core 应用
第一篇文章, 关于Mock的概念介绍: https://www.cnblogs.com/cgzl/p/9294431.html 第二篇文章, 关于方法Mock的介绍: https://www.cnbl ...
- 使用 Moq 测试.NET Core 应用 -- 其它
第一篇文章, 关于Mock的概念介绍: https://www.cnblogs.com/cgzl/p/9294431.html 第二篇文章, 关于方法Mock的介绍: https://www.cnbl ...
- 使用 Moq 测试.NET Core 应用 -- Mock 方法
第一篇文章, 关于Mock的概念介绍: https://www.cnblogs.com/cgzl/p/9294431.html 本文介绍使用Moq来Mock方法. 使用的代码: https://git ...
- 使用 Moq 测试.NET Core 应用 -- Mock 属性
第一篇文章, 关于Mock的概念介绍: https://www.cnblogs.com/cgzl/p/9294431.html 第二篇文章, 关于方法Mock的介绍: https://www.cnbl ...
- 使用 Moq 测试.NET Core 应用 -- Mock 行为
第一篇文章, 关于Mock的概念介绍: https://www.cnblogs.com/cgzl/p/9294431.html 第二篇文章, 关于方法Mock的介绍: https://www.cnbl ...
- Moq 测试 属性,常用方法
RhinoMock入门(7)——Do,With和Record-playback 摘要: (一)Do(delegate)有时候在测试过程中只返回一个静态的值是不够的,在这种情况下,Do()方法可以用来在 ...
- Moq 在.net Core 单元测试中的使用
Moq,主要用来伪造接口的实现类,实现方法,属性 moq The most popular and friendly mocking framework for .NET What? Moq (pro ...
- solr安装部署、solr测试创建core、用solrj 访问solr(索引和搜索)
一.安装solr4.8: 1.把apache-solr-4.8.1\example\webapps下的solr.war文件拷贝到Tomcat下的Tomcat7.0\webapps目录下,tomcat启 ...
- 利用BenchmarkDotNet 测试 .Net Core API 同步和异步方法性能
事由: 这两天mentor给我布置了个任务让我用BenchmarkDotNet工具去测试一下同一个API 用同步和异步方法写性能上有什么差别. 顺带提一下: 啊啊啊啊 等我仔细看文档的时候文档 发现它 ...
随机推荐
- 玩转web之ligerui(一)---ligerGrid重新指定url
请珍惜小编劳动成果,该文章为小编原创,转载请注明出处. 在特定情况下,我们需要重新指定ligerGrid的url来获取不同的数据,在这里我说一下我用的方法: 首先先定义一个全局变量,然后定义liger ...
- C++11标准中常用到的各种算法汇总.
在C++11标准中定义了很多算法,这些算法可以让我们很方便的操作各种容器和数组,这里要注意一下,这些算法操作的并非容器,而是迭代器,然后通过迭代器来操作容器中的数据,算法本身并不会关注容器中保存的数据 ...
- BZOJ_4726_[POI2017]Sabota?_树形DP
BZOJ_4726_[POI2017]Sabota?_树形DP Description 某个公司有n个人, 上下级关系构成了一个有根树.其中有个人是叛徒(这个人不知道是谁).对于一个人, 如果他 下属 ...
- VUE+webpack+npm项目中的RSA加解密
一.安装jsencrypt npm i jsencrypt node_modules文件夹中出现jsencrypt 二.引入jsencrypt 在main.js中import: import JsEn ...
- 【SAP HANA】新建账户和数据库(2)
开启HANA Studio,进入到User和Role的目录,这两个地方是创建账号和权限的. 新建用户 输入用户名和密码即可. 注意,如果系统里有同名的Catalog(数据库)存在的话,会报错,因为默认 ...
- Android 7.0 存储系统—Vold与MountService分析(一)(转 Android 9.0 分析)
Android的存储系统(一) 看了很长时间Vold存储模块的相关知识,也死扣了一段时间的Android源码,发现Android存储系统所涉及的函数调用,以及Kernel与上层之间的Socket传输真 ...
- LeetCode算法题-Subdomain Visit Count(Java实现)
这是悦乐书的第320次更新,第341篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第189题(顺位题号是811).像"discuss.leetcode.com& ...
- 初探机器学习之使用百度EasyDL定制化模型
一.Why 定制化模型 一般来说,各大云服务厂商只会提供一些最常见通用的AI服务,针对具体场景的AI应用则需要在云服务厂商提供的服务之上进行定制.例如,通常的图像识别只能做到分析照片的主题内容,而我的 ...
- Stackoverflow上有哪些声望高or值得关注的国人
Stackoverflow上有哪些声望高/值得关注的国人? 以下回答并不严格按照 Reputation 排名来列,也不收录不确定是Chinese(中国人或华人)的用户,欢迎补充- 1.李杨 @Li L ...
- WebView,我已经长大了,知道自己区分是否安全了!
一.前言 如果你在用 Android 原生系统(Google Play 服务),在使用 WebView 加载某些网页时,一定遇到过以下的安全警告红屏. 这是 WebView 的安全浏览保护策略,在 A ...