美文网首页scala工具集
mockito- 提升用例完整度利器(2)

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

作者: 小赵营 | 来源:发表于2019-11-05 19:37 被阅读0次

    本文是介绍提升测试完整度工具 的第二篇: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

    写在最后

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

    各位看官大大,赏个赞撒。

    相关文章

      网友评论

        本文标题:mockito- 提升用例完整度利器(2)

        本文链接:https://www.haomeiwen.com/subject/qebdvctx.html