当对代码进行测试的时候, 我们经常需要用到一些模拟(mock)技术.
绿色的是需要被测试的类, 黄色是它的依赖项, 灰色的无关的类
在一个项目里, 我们经常需要把某一部分程序独立出来以便我们可以对这部分进行测试. 这就要求我们不要考虑项目其余部分的复杂性, 我们只想关注需要被测试的那部分. 这里就需要用到模拟(Mock)技术.
因为, 请仔细看. 我们想要隔离测试的这部分代码对外部有一个或者多个依赖. 所以编写测试代码的时候, 我们需要提供这些依赖. 而针对隔离测试, 并不应该使用生产时用的依赖项, 所以我们使用模拟版本的依赖项, 这些模拟版依赖项只能用于测试时, 它们会使隔离更加容易.
绿色的是需要被测试的类, 黄色是Mock的依赖项
使用Mock技术, 可以有如下的优点:
Mock技术通常在单元测试中使用, 可以使用xUnit来为.NET Core应用做单元测试, 这里有介绍xUnit的文章: https://www.cnblogs.com/cgzl/p/9178672.html#xunit
那么什么是一个单元?
这个通常是由团队对系统的理解决定, 可以针对一个类, 也可以针对多个类.
单元测试通常具有以下特点:
还有其它的一些术语就不介绍了, 主要是这四个.
对于Stub 和 Mock ,可以看下面两张图例:
官网: 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的第一篇先到这.