前面介绍了单元测试的框架NUnit,它可以很好的帮助我们建立测试,检验我们的代码是否正确。但这还不够,有时候我们的业务比较重,会依赖其它的类。基于隔离测试的原则,我们不希望依赖的其它类影响到我们的测试目标。这时候Mock就显得十分重要了。当然还有其它因素使得我们必须Mock对象,比如配置文件,DB等。

提供Mock技术的工具很多:Moq,NSubstitute,RhinoMocks,TypeMock,JustMock等。开源免费的工具功能局限,像Moq,草根专栏 的博客写得很好。这里我选择JustMock,付费版本可以使用高级功能。

JustMock 开始

安装 JustMock ,从官网下载,默认安装。

添加 Telerik.JustMock.dll 引用,在安装目录下,默认为:C:\Program Files (x86)\Progress\Telerik JustMock\Libraries 。

开启使用高级功能

为什么需要Mock

先看我们需要测试的一个方法:

        /// <summary>
/// 转账
/// </summary>
/// <param name="accountA"></param>
/// <param name="accountB"></param>
/// <param name="money"></param>
/// <returns></returns>
public double TransferAccounts(BankAccount accountA, BankAccount accountB, double money)
{
double transferLimit = 50000.0;//转账最高限制
try
{
var balanceA = accountA.DrawMoney(money);
accountB.SaveMoney(money);
return balanceA;
}
catch (Exception ex)
{
throw new Exception($"转账失败,{ex.Message}");
}
}

测试这个方法的逻辑,只需要下面这段代码就可以了:

        private BankAccount bankAccountA;
private BankAccount bankAccountB;
[SetUp]
public void Setup()
{
bankAccountA = new BankAccount(1000);
bankAccountB = new BankAccount(1000);
} [Test]
public void Transfer_Test()
{
IBankService bankService = new BankService();
bankService.TransferAccounts(bankAccountA, bankAccountB, 500);
Assert.AreEqual(500, bankAccountA.GetBalance());
Assert.AreEqual(1500, bankAccountB.GetBalance());
}

但,如果转账的逻辑变了,需要判断是否超过当日限制,那么用户的转账总额就得从数据库或者其它途径获得了,那么可能代码变成这样子:

        private readonly IBankLimitDao _bankLimitDao;//获取限制条件的类

        public BankService(IBankLimitDao bankLimitDao)
{
_bankLimitDao = bankLimitDao;
} /// <summary>
/// 转账
/// </summary>
/// <param name="accountA"></param>
/// <param name="accountB"></param>
/// <param name="money"></param>
/// <returns></returns>
public double TransferAccounts(BankAccount accountA, BankAccount accountB, double money)
{
double transferLimit = 50000.0;//转账最高限制
try
{
//判断A是否能转账
var total = _bankLimitDao.TotalTransferTotal(accountA.AccountId);//获得限制金额
if (total >= transferLimit)
{
throw new Exception($"超过当日转账限额{transferLimit}");
}
var balanceA = accountA.DrawMoney(money);
accountB.SaveMoney(money);
return balanceA;
}
catch (Exception ex)
{
throw new Exception($"转账失败,{ex.Message}");
}
}

这个时候再用真实对象来测试就有点麻烦了。根据隔离原则,我们不希望测试 TotalTransferTotal 方法里的逻辑和它的正确性,它应该在其它地方测试。这时候Mock就显得重要了,我们可以模拟这个对象,并且给它一个恰当的值,让它“正确”执行。

所以,测试代码变成这样子:

        [Test]
public void Transfer_Test()
{
var bankLimit = Mock.Create<IBankLimitDao>();//模拟对象
Mock.Arrange(() => bankLimit.TodalDrawTotal(Arg.IsAny<string>())).Returns(500);//设定一个返回值
IBankService bankService = new BankService(bankLimit);
bankService.TransferAccounts(bankAccountA, bankAccountB, 500);
Mock.Assert(bankLimit);
Assert.AreEqual(500, bankAccountA.GetBalance());
Assert.AreEqual(1500, bankAccountB.GetBalance());
}

AAA

什么是AAA?Arrange、Act和Assert。AAA是单元测试中编写代码的模式。

  • Arrange:准备,设置需要测试的对象。
  • Act:执行测试的实际代码。
  • Assert:验证结果。

一个简单的例子:

这个例子包括创建模拟对象,标记为InOrder(),意为必须调用,执行方法,最后用Mock.Assert验证。

public interface IFoo
{
void Submit();
void Echo();
}
[Test]
public void ShouldVerifyCallsOrder()
{
// Arrange 模拟对象,并且设置条件
var foo = Mock.Create<IFoo>(); Mock.Arrange(() => foo.Submit()).InOrder();
Mock.Arrange(() => foo.Echo()).InOrder(); // Act 执行代码
foo.Submit();
foo.Echo(); // Assert 验证结果
Mock.Assert(foo);
}

编写测试方法的时候尽量遵循AAA的模式编写,可以让测试代码更清晰可读。

Mock Behaviors

JustMock 在Mock对象的时候有四种不同的行为可以选择。

  • RecursiveLoose Behavior

    默认的选项。模拟的对象不会出现null对象,递归调用也将创建一个默认的对象、默认值或者空值。
  • Loose Behavior

    除了设置值,否则Loose创建的对象将是默认值。
  • CallOriginal Behavior

    将会采用最初的模拟对象。
  • Strict Behavior

    采用此行为,模拟对象必须设置值,否则会出现 MockException异常。

下面代码展示不同类型的结果:

        [Test]
public void Test()
{
// Arrange
var rlFoo = Mock.Create<FooBase>(Behavior.RecursiveLoose);
var lFoo = Mock.Create<FooBase>(Behavior.Loose);
var coFoo = Mock.Create<FooBase>(Behavior.CallOriginal);
var sFoo = Mock.Create<FooBase>(Behavior.Strict); Mock.Arrange(() => rlFoo.GetString("y")).Returns("z");
Mock.Arrange(() => lFoo.GetString("y")).Returns("z");
Mock.Arrange(() => coFoo.GetString("y")).Returns("z");
Mock.Arrange(() => sFoo.GetString("y")).Returns("z"); // Act
var rlactualX = rlFoo.GetString("x"); // 结果:""
var rlactualY = rlFoo.GetString("y"); // 结果:"z" var lactualX = lFoo.GetString("x"); // 结果:null
var lactualY = lFoo.GetString("y"); // 结果:"z" var coactualX = coFoo.GetString("x"); // 结果:"x"
var coactualY = coFoo.GetString("y"); // 结果:"z"
var coactualA = coFoo.GetString("a"); // 结果:"a" //var sactualX = sFoo.GetString("x"); // 结果:出现异常
var sactualY = sFoo.GetString("y"); // 结果:"z" var expectedX = "x";
var expectedY = "z"; // Assert
Assert.AreEqual(expectedX, rlactualX);
Assert.AreEqual(expectedY, rlactualY);
}

本篇到这,下篇再记录一些其它用法。

.net core 单元测试之 JustMock第一篇的更多相关文章

  1. .net core 单元测试之 JustMock第二篇

    JustMock标记方法 上篇文章在举例子的时候使用了returns的标记方法,JustMock还有很多标记方法: CallOriginal 跟Behaviors里的CallOriginal差不多意思 ...

  2. Core Animation 文档翻译 (第一篇)

    Core Animation 文档翻译(第一篇)   2018-01-13  星期6 前言:作为iOS 开发,官方文档的阅读是很有必要的,值此周末便写下此文.作为iOS 实际经验3年的开发,之前的应用 ...

  3. Core Animation文档翻译 (第一篇)

    Core Animation 文档翻译(第一篇) 前言 作为iOS 开发,官方文档的阅读是很有必要的,值此周末便写下此文.作为iOS 实际经验3年的开发,之前有阅读并实践过经典的<iOS核心动画 ...

  4. .NET Core单元测试之搞死开发的覆盖率统计(coverlet + ReportGenerator )

    .NET Core单元测试之搞死开发的覆盖率统计 这两天在给项目补单元测试,dalao们要求要看一下测试覆盖率 翻了一波官方test命令覆盖率倒是有支持了,然而某个更新日志里面写着 ["Su ...

  5. ASP.NET Core搭建多层网站架构【3-xUnit单元测试之简单方法测试】

    2020/01/28, ASP.NET Core 3.1, VS2019, xUnit 2.4.0 摘要:基于ASP.NET Core 3.1 WebApi搭建后端多层网站架构[3-xUnit单元测试 ...

  6. ASP.NET Core搭建多层网站架构【12-xUnit单元测试之集成测试】

    2020/02/01, ASP.NET Core 3.1, VS2019, xunit 2.4.1, Microsoft.AspNetCore.TestHost 3.1.1 摘要:基于ASP.NET ...

  7. ASP.NET Core 学习笔记 第一篇 ASP.NET Core初探

    前言 因为工作原因博客断断续续更新,其实在很早以前就有想法做一套关于ASP.NET CORE整体学习度路线,整体来说国内的环境的.NET生态环境还是相对比较严峻的,但是干一行爱一行,还是希望更多人加入 ...

  8. [转载]单元测试之道(使用NUnit)

    首先来看下面几个场景你是否熟悉 1.你正在开发一个系统,你不断地编码-编译-调试-编码-编译-调试……终于,你负责的功能模块从上到下全部完成且编译通过!你长出一口气,怀着激动而又忐忑的心情点击界面上的 ...

  9. 单元测试之道(使用NUnit)

    首先来看下面几个场景你是否熟悉 1.你正在开发一个系统,你不断地编码-编译-调试-编码-编译-调试……终于,你负责的功能模块从上到下全部完成且编译通过!你长出一口气,怀着激动而 又忐忑的心情点击界面上 ...

随机推荐

  1. 个人永久性免费-Excel催化剂功能第47波-VBA开发者喜爱的加密函数类

    VBA的确是个很不错的编程工具,寄生在OFFICE内,无需安装庞大的开发环境,即开即用,方便灵活,能实现的事情也很多,但毕竟VBA是微软停止更新维护将近20年的一种语言,计算机的世界发展速度有多快大家 ...

  2. Hive调优策略

    Hive调优策略 Fetch抓取 Fetch抓取是指,Hive中对某些情况的查询可以不必使用MapReduce计算. 例如:select * from employee:在这种情况下,Hive可以简单 ...

  3. GStreamer基础教程05 - 播放时间控制

    简介 在多媒体应用中,我们通常需要查询媒体文件的总时间.当前播放位置,以及跳转到指定的时间点.GStreamer提供了相应的接口来实现此功能,在本文中,我们将通过示例了解如何查询时间信息,以及如何进行 ...

  4. 【微信小程序】微信小程序-实现tab

    一.前言 小程序开发中,有很多封装好的控件供开发者使用,但是,很常见的tab选项卡居然没有,只能自己搞一个. 实现原理也很简单,无非是用给view(tab)设置一个点击事件bintap,并且给view ...

  5. Linux版本划分——基于打包方式

    基于Dpkg (Debian系) Debian GNU / Linux是一种强调使用自由软件的发行版.它支持多种硬件平台.Debian及其派生发行版使用deb软件包格式,并使用dpkg及其前端作为包管 ...

  6. linux初学者-SElinux篇

    linux初学者-SElinux篇 SElinux是强制访问控制(MAC)安全系统,是linux历史上最杰出的新安全系统.对于linux安全模块来说,SElinux的功能是最全面的,测试也是最充分的, ...

  7. 手动创建MySQL服务

    1.复制一份MySQL服务文件,放入一个路径 2.清理data文件夹下文件,仅保留mysql等 3.修改my.ini,port,...dir等配置 4.管理员运行cmd,cd>bin:mysql ...

  8. 二进制文件安装安装etcd

    利用二进制文件安装安装etcd etcd组件作为一个高可用强一致性的服务发现存储仓库. etcd作为一个受到ZooKeeper与doozer启发而催生的项目,除了拥有与之类似的功能外,更专注于以下四点 ...

  9. 浅入深出Vue:自动化路由

    在软件开发的过程中,"自动化"这个词出现的频率是比较高的.自动化测试,自动化数据映射以及各式的代码生成器.这些词语的背后,也说明了在软件开发的过程中,对于那些重复.千篇一律的事情. ...

  10. 一文读懂tomcat组件--一个web服务器的架构演化史

    1. tomcat是谁?     2. tomcat可以做什么? tomcat是一个web容器,可以将web应用部署到tomcat,由它提供web服务,一个web容器中可以部署多个web应用,这些we ...