mockito- 提升用例完整度利器(2)

本文是介绍提升测试完整度工具 的第二篇:mockito -- 一个源于java的测试框架

mock测试在上篇已经安利了。这次用图来表示下,大家提味下mock的含义。

  • 类A功能是对学生成绩进行排名,成绩需要学校的数据库。我们为了测试排名的功能,需要访问数据库,但这在测试过程时,并不是我们期望的。mockito帮助我们不查询数据库情况下,完成对功能进行测试。

  • 见下图类A 功能实现依赖B,C.类B 依赖D。如果要测试A的功能,需要BC被调用,mock的功能就是在BC不被调用情况下,完成对A的测试。

mock-before.PNG

如下图,使用mock工具是对类B/C处理,模拟BC提供的功能。通过模拟类的功能,让我们能够专心的构建测试场景,忽略外部依赖。

mock-after.PNG

以上是mock基本使用思路。

mockito 介绍

在java测试框架领域,mockito绝对是大名鼎鼎。大多数的mock测试框架如EasyMock和ScalaMock都是expect-run-verify,也是我们上篇提到使用方式。而mockito拥有更简单、更直观api。mockito的设计理念是是尽量少的api,让用户更专注使用场景。

今天,我们重点是mockito版本scala版本(mockito-scala)。它延续了mockito的易用性,又基于scala订制更多的语法糖,让scala的开发者使用起来更加方便。接下来,我们一起走进mockito的世界。

用例运行环境

那句老话:一切没有预置前提说明文档都必然是耍流氓,_

//scala test inter scala mock sugar
// https://mvnrepository.com/artifact/org.mockito/mockito-scala
libraryDependencies += "org.mockito" %% "mockito-scala" % "1.5.16" % Test
// https://mvnrepository.com/artifact/org.mockito/mockito-scala-scalatest
libraryDependencies += "org.mockito" %% "mockito-scala-scalatest" % "1.5.16" % Test

本文用例基于 mockito-scala 1.5.16scalatest 3.0.8的版本。

mockito 使用示例

我们使用了上一篇文档相同类进行测试,构造相同的场景,以便进行2个mock测试框架的异同。

mockito运行基于 scalatest,我们编写的用例形式上要遵循scalatest的形式。在介绍使用场景前,先看看mockito语法样式。

  • mockito语法样式

// mock 是mockito关键字
//mock trait定义变量m1
val m1: MockObjectInTrait = mock[MockObjectInTrait]
//格式:条件函数 + 返回函数:如 when(m1.函数) thenReturn()
when(m1.getObjectInfo) thenReturn("mock info") //具体代码

从语法结构上看似乎比scalamock更好。

  • 测试用例定义

class MyMockTestTest extends UnitSpec with MockitoSugar

mockito需要继承MockitoSugar类才能运行以下测试用例。

mock函数

mockito不支持对函数的mock。这区别与scalamock.

基于trait 的mock

trait进行mock操作关键字是:mock.这点和scalamock是一致的。

源码如下:

trait MyMockTest {
def funcOnePara(name: String): String = {
name
}

def funcTwoPara(name: String, age:Int): String = name
def funcNoArgs: String = "func no args"
val valPara="variable param"
}

  • mock trait 示例

    • mock trait无参数示例

    val m = mock[MyMockTest]
    when(m.funcNoArgs) thenReturn ("mockito mock") //调用无参时,返回指定字符串
    println(m.funcNoArgs)
    m.funcNoArgs //没有指定调用次数的接口

    when .. then..用例编写风格,是不是比scalamock更易读哈。

    • trait有参调用mock

when(m.funcOnePara("xiaozhaoying")) thenReturn("mock zxy")

println(m.funcOnePara("xiaozhaoying")) //output mock zxy</pre>

基于class 的mock

和mocktrait一样,class mock是使用更多的一种场景。让我们来看看如何对class进行mock操作

class 进行mock操作关键字是:mock.

//源码示例

class MyMockClass {
var age: Int = 0
def funcMock = ""

def this(age:Int) { //辅助构造函数
this
this.age = age
}
}</pre>

  • classmock操作示例

val mClass = mock[MyMockClass] //mock类
when(mClass.age) thenReturn(16) //设定变量值
println(mClass.age) //return 16
when(mClass.age) thenCallRealMethod() //调用真实值
println(mClass.age) //return 0
//mock 函数
when(mClass.funcMock) thenReturn ("func mock")
println(mClass.funcMock)//return func mock</pre>

从类的mock示例来看,我们看到: 1.mockito 支持对var变量的mock. 2.对函数mock方式看,类和trait没有区别 3.有新api- thenCallRealMethod ,即使mock变量/函数,依然能够调用真实实现。这在显示使用场景中减少代码量和实现难度。

基于case class 的mock

相比于scalamock对case classmock的吃相难看, mockito更优雅。示例代码:

//mock case class
val mockCase: Person = mock[Person]
when(mockCase.age) thenReturn (12)
println(mockCase.age)</pre>

我们看到和trait,class的mock方式并无二样。

同样,mockito不支持带参数类的mock.

基于class mock其它应用

源码如下:

class MockClassInTrait {
def getArgs(name: String) = "with args"
def getArgs(name: String, age: Int): String = s"name +age"

def get(name: String, age: Int): String = s"name +age"
}

示例代码函数进行重载,对函数的mock使用上文方法是理所当然的支持。我们下面要看到的是mockito对函数重载提供的新特性。 示例:

val m1: MockClassInTrait = mock[MockClassInTrait]
// 1.对于任意参数,返回相同值
when(m1.getArgs(any[String])).thenReturn("mock with param")
println(m1.getArgs(""))
//2.对于不同参数,返回不同值。对函数进行重写
when(m1.getArgs(any[String], any[Int])).thenAnswer((name: String, age: Int) => s"$name ")
println(m1.getArgs("then answer", 18))
//3.对于不同参数,调用原始实现
when(m1.getArgs(any[String], any[Int])).thenCallRealMethod()
println(m1.getArgs("then answer", 18))</pre>

mockito涵盖函数调用的3种行为方式,让mock使用更加广泛和更具备可操作性。

部分mock场景支持的特性- spy

部分mock(partial mock)是说一个类的方法有些是实际调用,有些需要对函数进行mock.mockito给的解决方法是除了上文的thenCallRealMethod(),还有就是spy方式。 spy方式提供不少新的特性,我们只简单描述提其基本特性。我们依然以MockClassInTrait类为例看看,如何使用spy特性。

val ms = spy(new MockClassInTrait, lenient = true)
println(ms.getArgs("")) //1.调用真实函数
//2.对函数进行mock
when(ms.getArgs("mock")).thenReturn("mock using spy")
println(ms.getArgs("mock"))
//3.调用真实函数
println(ms.getArgs(""))
//4.verify 校验ms变量函数执行次数
println(verify(ms, times(0)).getStr()) </pre>

其它特性

mockito在可用性上有更多值得我们发掘的功能,同性交友社区github的官方文档可参考。链接: https://github.com/mockito/mockito-scala

写在最后

不言而喻,我们借助工具提升代码质量。但付出的成本是不同的,可测试性代码或易测试的代码性价比更高。在我们提升代码质量措施上,我们心理都知道性价比最高的是哪一种?但项目决策远非软件工程所定义的那样。借用别人一句话:从统计的角度来看,大多数严格遵守软件工程理论的公司没产生多少杰出软件;大多数杰出软件和严格的遵守软件工程没什么关系。

各位看官大大,赏个赞撒。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容