使用 specs 測試

2022-05-13 10:38 更新

擴展規(guī)格

讓我們直接開始。

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(算術(shù)) 是一個 規(guī)范約束下的系統(tǒng)

add(加) 是一個上下文。

add two numbers(兩個數(shù)相加),和 add three numbers(三個數(shù)字相加) 是例子。

mustEqual 表示 預(yù)期

1 mustEqual 1 是編寫實際測試前使用的一種常見的預(yù)期占位符。所有的測試用例都應(yīng)該至少有一個預(yù)期。

復(fù)制

注意到兩個測試都是怎樣將 add 加在其名稱中的嗎?我們可以通過嵌套預(yù)期擺脫這種重復(fù)。

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
      }
    }
  }
}

執(zhí)行模型

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 只能運行在葉子用例上。

doFirst & doLast

doFirst/doLast 用來做一次性的設(shè)置。(需要例子,我不使用這個)

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

Matchers

你有數(shù)據(jù),并且想要確保它是正確的。讓我們看看最常用的匹配器是如何幫助你的。

mustEqual

我們已經(jīng)看到幾個 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 must haveKey(k)
map must notHaveKey(k)

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

數(shù)字

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

a must throwA[WhateverException]

這是一個針對try\catch塊中有異常拋出的用例的簡寫。

您也可以期望一個特定的消息

a must throwA(WhateverException("message"))

您也可以匹配異常:

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

編寫你自己的匹配器

import org.specs.matcher.Matcher

作為一個不變量

"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
  }
}

契約是返回一個包含三個值的元組,分別是期望是否為真、為真時的消息和為假時的消息。

作為一個樣本類

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))
}

使用樣本類可以增加代碼的重用性。

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

Spies(間諜)可以對真正的對象做一些“局部 mocking”:

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

// methods can be stubbed on a spy
spiedList.size returns 100

// other methods can also be used
spiedList.add("one")
spiedList.add("two")

// and verification can happen on a spy
there was one(spiedList).add("one")

然而,使用間諜可能會出現(xiàn)非常詭異的情況:

// if the list is empty, this will throws an IndexOutOfBoundsException
spiedList.get(0) returns "one"

這里必須使用 doReturn :

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

在 sbt 中運行單個 specs

> test-only com.twitter.yourservice.UserSpec

將只運行那個規(guī)范。

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

將在一個循環(huán)中運行該測試,文件的每一次修改都將觸發(fā)測試運行。

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號