使用Specs进行单元测试

这个章节的内容包含使用Specs进行测试,同时介绍了Scala的行为驱动设计(Behavior-Driven Desing BBD)的框架。

 

继承Specification

让我们直接开始吧!

import org.specs._

object ArithmeticSpec extends Specification {
  "Arithmetic" should {
    "add two numbers" in {
      1 + 1 mustEqual 2
    }
    "add three numbers" in {
      1 + 1 + 1 mustEqual 3
    }
  }
}

Arithmetic是“基于指定规范(specification)的系统”

add是一个上下文。

add two numbersadd threes numbers都是示例。

mustEqual表示一个期望(expectation)

在你真正开始写测试之前,1 mustEqual 1只是你的期望的一个占位符。所有的示例(example)最少要有一个期望(expectation)。

 

重复

注意在两个测试用例的名称中都有add。我们可以通过嵌套的期望(expectation)来消除重复的命名。

import org.specs._

object ArithmeticSpec extends Specification {
  "Arithmetic" should {
    "add" in {
      "two numbers" in {
        1 + 1 mustEqual 2
      }
      "three numbers" in {
        1 + 1 + 1 mustEqual 3
      }
    }
  }
}

 

执行模型

object ExecSpec extends Specification {
  "Mutations are isolated" should {
    var x = 0
    "x equals 1 if we set it." in {
      x = 1
      x mustEqual 1
    }
    "x is the default value if we don't change it" in {
      x mustEqual 0
    }
  }
}

 

用例的设置(Setup)与清理(Teardown)

doBefore 和 doAfter

"my system" should {
  doBefore { resetTheSystem() /** user-defined reset function */ }
  "mess up the system" in {...}
  "and again" in {...}
  doAfter { cleanThingsUp() }
}

注意 doBefore/doAfter只会在叶子示例(leaf example)上运行。

 

doFirst 和 doLast

doFirst/doLast主要用于一次性的设置。(需要示范吗,不过我不使用它们)

"Foo" should {
  doFirst { openTheCurtains() }
  "test stateless methods" in {...}
  "test other stateless methods" in {...}
  doLast { closeTheCurtains() }
}

 

匹配器(Matchers)

你有一些数据,并且想要验证它是否正确。我们可以使用matcher。下面我们来看看常用的一些matcher。(参考匹配器指南

mustEqual

我们前面已经见过一些使用mustEqual的例子。

1 mustEqual 1

"a" mustEqual "a"

可以判断引用或者值是否相同。

序列里的值

val numbers = List(1, 2, 3)

numbers must contain(1)
numbers must not contain(4)

numbers must containAll(List(1, 2, 3))
numbers must containInOrder(List(1, 2, 3))

List(1, List(2, 3, List(4)), 5) must haveTheSameElementsAs(List(5, List(List(4), 2, 3), 1))

Map里的元素

map must haveKey(k)
map must notHaveKey(k)

map must haveValue(v)
map must notHaveValue(v)

数字

a must beGreaterThan(b)
a must beGreaterThanOrEqualTo(b)

a must beLessThan(b)
a must beLessThanOrEqualTo(b)

a must beCloseTo(b, delta)

Options

a must beNone

a must beSome[Type]

a must beSomething

a must beSome(value)

throwA

这个比使用带有fail的try catch简洁多了。

你也可以加上一个特定消息的判定。

a must throwA(WhateverException("message"))

你也可以对期望进行match匹配:

a must throwA(new Exception) like {
  case Exception(m) => m.startsWith("bad")
}

编写你自己的匹配器

import org.specs.matcher.Matcher

作为一个val

"A matcher" should {
  "be created as a val" in {
    val beEven = new Matcher[Int] {
      def apply(n: => Int) = {
        (n % 2 == 0, "%d is even".format(n), "%d is odd".format(n))
      }
    }
    2 must beEven
  }
}

这里对于返回值有一个约定,返回的结果是一个元组,它包含这个期望(expectation)是否为true,以及在不为true的情况下的消息。

作为一个case class

case class beEven(b: Int) extends Matcher[Int]() {
  def apply(n: => Int) =  (n % 2 == 0, "%d is even".format(n), "%d is odd".format(n))
}

使用case class 可以提高它的共用性。

 

Mocks

import org.specs.Specification
import org.specs.mock.Mockito

class Foo[T] {
  def get(i: Int): T
}

object MockExampleSpec extends Specification with Mockito {
  val m = mock[Foo[String]]

  m.get(0) returns "one"

  m.get(0)

  there was one(m).get(0)

  there was no(m).get(1)
}

参考 Using Mockito

 

Spies

Spy可以用来对一个具体的对象进行“部分mock”:

val list = new LinkedList[String]
val spiedList = spy(list)

//在spy里,一个方法返回值可以被替代
spiedList.size returns 100

// 同时其他的方法可以正常使用
spiedList.add("one")
spiedList.add("two")

// 然后可以在spy上进行验证

there was one(spiedList).add("one")

 

不过,使用spy需要一些技巧:

//如果列表为空的话,下面的操作会抛出IndexOutOfBoundsException
spiedList.get(0) returns "one"

 

可以通过doReturn来处理上面的场景:

doReturn("one").when(spiedList).get(0)

 

在sbt里单独运行指定的spec

> test-only com.twitter.yourservice.UserSpec

这样会只运行指定的spec。

> ~ test-only com.twitter.yourservice.UserSpec

 

这样的话,会根据文件的改动来不停地触发这个测试用例的运行。

原文链接: Scala School 翻译: ImportNew.com - 朱伟杰
译文链接: http://www.importnew.com/4593.html
[ 转载请保留原文出处、译者和译文链接。]

关于作者: 朱伟杰

Java开发工程师,业余翻译

查看朱伟杰的更多文章 >>



相关文章

发表评论

Comment form

(*) 表示必填项

1 条评论

  1. 浮生 说道:

    非常好的文章,正在学习Specs2。非常有参考价值,希望楼主多分享这方面的文章!

    Thumb up 2 Thumb down 0

跳到底部
返回顶部