JustMock标记方法

上篇文章在举例子的时候使用了returns的标记方法,JustMock还有很多标记方法:

  • CallOriginal

    跟Behaviors里的CallOriginal差不多意思,被调用时执行原始的方法和属性的实现。
  • DoNothing

    忽略对方法或者属性的调用。
  • DoInstead

    替换原来方法的调用,或者属性的设置。
  • MustBeCalled

    被标记的方法必须调用,否则会抛出异常。
  • Raise

    当我们需要测试Event是否执行时,我们可以使用Raise来引发Events。

    例子:
    public delegate void CustomEvent(string value);

    public interface IFoo
{
event CustomEvent CustomEvent;
}
        //测试方法:
[Test]
public void ShouldInvokeMethodForACustomEventWhenRaised()
{
string expected = "ping";
string actual = string.Empty; var foo = Mock.Create<IFoo>(); foo.CustomEvent += delegate (string s)
{
actual = s;
}; //引发事件,并传递参数
Mock.Raise(() => foo.CustomEvent += null, expected); Assert.AreEqual(expected, actual);
}
  • Raises

    Raises用在调用方法后出发事件。

    例子:
    //使用的接口
public delegate void CustomEvent(string value, bool called); public delegate void EchoEvent(bool echoed); public delegate void ExecuteEvent(); public interface IFoo
{
event CustomEvent CustomEvent;
event EchoEvent EchoEvent;
event ExecuteEvent ExecuteEvent; void RaiseMethod();
string Echo(string arg);
void Execute();
void Execute(string arg);
}

当调用方法时触发:

    //测试方法
[Test]
public void ShouldRaiseCustomEventOnMethodCall()
{
string actual = string.Empty;
bool isCalled = false; var foo = Mock.Create<IFoo>(); foo.CustomEvent += (s, c) => { actual = s; isCalled = c; };
Mock.Arrange(() => foo.RaiseMethod()).Raises(() => foo.CustomEvent += null, "ping", true); foo.RaiseMethod();//调用方法,触发事件 Assert.AreEqual("ping", actual);
Assert.IsTrue(isCalled);
}

另外还可以设置参数满足条件时调用的方法触发,设置延时触发。

  • Returns

    设置返回结果。
  • Throws

    当一个方法调用时,抛出异常。

    例子:
        [Test]
public void ShouldThrowExceptionOnMethodCall()
{
// Arrange
var foo = Mock.Create<IFoo>(); Mock.Arrange(() => foo.Execute(string.Empty)).Throws<ArgumentException>(); Assert.Throws<ArgumentException>(() => foo.Execute(string.Empty));
}
  • ThrowsAsync

    调用异步方法时抛出异常。(官方文档有这个标记,貌似使用21912071版本时没有这个标记了)
  • When

    当满足条件时执行某操作。

    例子:
    public interface IFoo
{
bool IsCalled();
string Prop { get; set; }
}
        [Test]
public void IsCalled_ShouldReturnTrue_WithMockedDependencies()
{
var foo = Mock.Create<IFoo>(); Mock.Arrange(() => foo.Prop).Returns("test");//设置Prop返回"Test"
Mock.Arrange(() => foo.IsCalled()).When(() => foo.Prop == "test").Returns(true); Assert.IsTrue(foo.IsCalled());
}

JustMock模拟属性

上面的例子中已经用到Returns来模拟属性的值了,这里再看看还有其它的用法:

  • 根据索引模拟值

    假设一个类的属性是数组或者链表等,我们需要模拟其中某个索引下的值:
//indexedFoo是一个数组
Mock.Arrange(() => indexedFoo[1]).Returns("pong"); //Mock indexedFoo 下标1的值为“pong”
  • 设置属性

    我们需要验证一个属性是否被正确赋值时,可以用 ArrangeSet 来模拟设置属性并验证。
[Test]
public void ShouldAssertPropertySet()
{
var foo = Mock.Create<IFoo>(); Mock.ArrangeSet(() => foo.Value = 1); foo.Value = 1; //执行赋值 Mock.AssertSet(() => foo.Value = 1);
}

Matchers 匹配参数

我们模拟有参数的方法时,要根据不同的参数设置不同的返回值,Matchers可以做到这些。

  1. Arg.AnyBool,Arg.AnyDouble,Arg.AnyFloat, Arg.AnyGuid, Arg.AnyInt, Arg.AnyLong, Arg.AnyObject, Arg.AnyShort, Arg.AnyString, Arg.NullOrEmpty
  2. Arg.IsAny()
  3. Arg.IsInRange([FromValue : int], [ToValue : int], [RangeKind])
  4. Arg.Matches(Expression<Predicate> expression)
  5. Arg.Ref()
  6. Args.Ignore()

我们可以利用Arg.Matches<T>(Expression<Predicate<T>> expression) 生成我们想要的数据条件。

Arg.Ref() 是C#里的ref 参数。

Args.Ignore() 可以忽略参数,同标记方法IgnoreArguments()

Matchers 不仅在构造参数中使用,还可以在Assert中使用:

    [Test]
public void ShouldUseMatchersInAssert()
{
var paymentService = Mock.Create<IPaymentService>(); paymentService.ProcessPayment(DateTime.Now, 54.44M); // 断言:不管DateTime什么时间,Payment amount都是54.44.
Mock.Assert(() => paymentService.ProcessPayment(
Arg.IsAny<DateTime>(),
Arg.Matches<decimal>(paymentAmount => paymentAmount == 54.44M)));
}

Sequential Mocking 连续模拟

Sequential mocking 允许我们多次执行代码返回不一样的值,具体代码理解:

[TestMethod]
public void ShouldArrangeAndAssertInASequence()
{
var foo = Mock.Create<IFoo>();
//只需要在Arrange()后面加上InSequence()
Mock.Arrange(() => foo.GetIntValue()).Returns(0).InSequence();
Mock.Arrange(() => foo.GetIntValue()).Returns(1).InSequence();
Mock.Arrange(() => foo.GetIntValue()).Returns(2).InSequence(); int actualFirstCall = foo.GetIntValue(); //结果是:0
int actualSecondCall = foo.GetIntValue(); //结果是:1
int actualThirdCall = foo.GetIntValue(); //结果是:2
int actualFourthCall = foo.GetIntValue(); // 注意这是第四次调用,因为没有设置结果,实际上他应当是上次调用的结果:2
}

配合Matcher使用:

 Mock.Arrange(() => foo.Echo(Arg.Matches<int>(x => x > 10))).Returns(10).InSequence();
Mock.Arrange(() => foo.Echo(Arg.Matches<int>(x => x > 20))).Returns(20).InSequence(); int actualFirstCall = foo.Echo(11); //结果10
int actualSecondCall = foo.Echo(21); //结果20

还可以多次返回:

Mock.Arrange(() => foo.Echo(Arg.AnyInt)).Returns(10).Returns(11).MustBeCalled(); 

Assert.AreEqual(10, foo.Echo(1));
Assert.AreEqual(11, foo.Echo(2));

或者以数组方式返回:

int[] returnValues = new int[3] { 1, 2, 3 }; 

Mock.Arrange(() => foo.Echo(Arg.AnyInt)).ReturnsMany(returnValues); 

var actualFirstCall = foo.Echo(10); // 结果:1
var actualSecondCall = foo.Echo(10); // 结果:2
var actualThirdCall = foo.Echo(10); // 结果:3

Recursive Mocking 递归模拟

递归Mock。有时候我们需要取值像这样的:foo.Bar.Baz.Do("x"),我们可以不用一个一个对象去模拟,我们只需要Mock 第一个,然后设置值就可以了。

像这样:

 var foo = Mock.Create<IFoo>(); // mock 第一个对象

 Mock.Arrange(() => foo.Bar.Do("x")).Returns("xit"); //设置某次调用的值
Mock.Arrange(() => foo.Bar.Baz.Do("y")).Returns("yit");

最后

这里涉及到的都是是JustMock的免费版功能。在工作中遇到一些问题,我们需要测试的目标方法中混有静态类提供的逻辑,免费的框架都不支持Mock静态类,需要用到付费版的高级功能。更好的时候,我们应该避免这些依赖,以方便我们测试。

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

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

    前面介绍了单元测试的框架NUnit,它可以很好的帮助我们建立测试,检验我们的代码是否正确.但这还不够,有时候我们的业务比较重,会依赖其它的类.基于隔离测试的原则,我们不希望依赖的其它类影响到我们的测试 ...

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

    Core Animation 文档翻译 (第二篇) 核心动画基础要素 核心动画为我们APP内Views动画和其他可视化元素动画提供了综合性的实现体系.核心动画不是我们APP内Views的替代品,相反, ...

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

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

  4. 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单元测试 ...

  5. 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 ...

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

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

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

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

  8. 第二篇 界面开发 (Android学习笔记)

    第二篇 界面开发 第5章 探索界面UI元素 ●The Android View Class     ●△Widget设计步骤 需要修改三个XML,以及一个class: 1)第一个xml是布局XML文件 ...

  9. .NET Core资料精选:架构篇

    .NET 6.0 马上就要发布,高性能云原生开发框架.希望有更多的小伙伴加入大.NET阵营.这是本系列的第三篇文章:架构篇,喜欢的园友速度学起来啊. 本系列文章,主要分享一些.NET Core比较优秀 ...

随机推荐

  1. Mybatis方法入参处理

    1,在单个入参的情况下,mybatis不做任何处理,#{参数名} 即可,甚至连参数名都可以不需要,因为只有一个参数,或者使用 Mybatis的内置参数 _parameter. 2,多个入参: 接口方法 ...

  2. Hyperledger Fabric 之 Channel ,创建channel链接几项注意点

    好长时间没有更新博客,网上也有很多fabric的部署资料,而且也都很不错,也比较全面.我就再想重复的工作暂时就不用做了,后面抽时间在做细化和分类:就将学习和工作中遇到和解决的问题经验,做一些分享. 而 ...

  3. Spring集成Shiro使用小结

    shiro的认证流程 Application Code:应用程序代码,由开发人员负责开发的 Subject:框架提供的接口,代表当前用户对象 SecurityManager:框架提供的接口,代表安全管 ...

  4. [小米OJ] 7. 第一个缺失正数

    思路: 参考这个思路 即:将每个数字放在对应的第几个位置上,比如1放在第1个位置上,2放在第2个位置上. 注意几个点:将每个数放在它正确的位置,前提是该数是正数,并且该数小于序列长度,并且交换的两个数 ...

  5. 基于 HTML5 WebGL 的民航客机飞行监控系统

    前言 前些日子出差,在飞机上看到头顶的监控面板,除了播放电视剧和广告之外,还会时不时的切换到一个飞机航行的监控系统,不过整个监控系统让人感到有一点点的简陋,所以我就突发奇想制作了一个采用 HT for ...

  6. python课堂整理19----迭代器和生成器

    一.概念 • 迭代器协议: 对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么引起一个stopIteration异常,以终止迭代(只能往后走,不能往前退) • 协议是一种约定,pyt ...

  7. 《HelloGitHub》第 40 期

    <HelloGitHub>第 40 期 兴趣是最好的老师,HelloGitHub 就是帮你找到兴趣! 简介 分享 GitHub 上有趣.入门级的开源项目. 这是一个面向编程新手.热爱编程. ...

  8. Mysql优化(出自官方文档) - 第五篇

    目录 Mysql优化(出自官方文档) - 第五篇 1 GROUP BY Optimization 2 DISTINCT Optimization 3 LIMIT Query Optimization ...

  9. UIRefreshControl 问题

    这两天在学UIRefreshControl,主要参照的是github上的一个Demo(网址 https://github.com/evgeniymikholap/UIRefreshControlExa ...

  10. 用mongodb 固定集合实现只保留固定数量的记录,自动淘汰老旧数据

    在一个保存report记录的场景中,我们使用MongoDB进行数据存储 example: db: report Collection: daily_report 创建db:  use report; ...