1. 程式人生 > >[scala斷言專欄]--選擇專案的測試樣式

[scala斷言專欄]--選擇專案的測試樣式

英文原文:

ScalaTest支援不同風格的測試,每種測試都旨在滿足特定的需求。為了幫助您找到適合您專案的最佳樣式,本頁將介紹每個選項的預期用例。

我們建議您為每個專案選擇一組測試樣式,然後鼓勵在專案上工作的所有人使用所選擇的樣式。這允許測試樣式適合團隊,同時保持專案程式碼庫的一致性。我們建議您選擇一種主要樣式進行單元測試,另一種用於驗收測試。使用不同的樣式進行單元和驗收測試可以幫助開發人員在低階單元測試與高階驗收測試之間進行“切換”。您也可以選擇在特殊情況下使用的特定樣式,例如 PropSpec用於測試矩陣。我們通常編寫整合測試 - 涉及子系統(例如資料庫)的測試 - 與單元測試相同的風格。

簡而言之,ScalaTest的靈活性並不是為了使個別開發人員能夠在同一個專案中使用不同的測試樣式。相反,它旨在使專案負責人為團隊選擇最適合的風格或風格。如果您在執行所選樣式時遇到問題,可以在構建中

指定選定的樣式

你所選擇的風格只會指示你的測試的宣告。ScalaTest-assertions,matchers ,mixin traits 等中的其他一切 - 無論您選擇什麼樣式,工作始終如一。

如果你不喜歡購物

如果您寧願被告知採用哪種方法,而不是選擇一種方法,我們建議您使用 FlatSpec單元和整合測試以及 FeatureSpec驗收測試。我們建議FlatSpec作為預設選擇,因為它是平面(不需要的)像大多數開發人員熟悉的XUnit測試,但是會引導您使用描述性的規範樣式名稱編寫集中的測試。

風格特質用例

如果您寧願做出自己的選擇,該表格可以快速概述每種風格特徵的優缺點。有關更多資訊和示例,請單擊連結:

樣式舉例和描述

FunSuite(個人非常推薦使用)

For teams coming from xUnit, FunSuite feels comfortable and familiar while still giving some of the benefits of BDD: 

FunSuite makes it easy to write descriptive test names, natural to write focused tests, 

and generates specification-like output that can facilitate communication among stakeholders.

import org.scalatest.FunSuite

class SetSuite extends FunSuite {

  test("An empty Set should have size 0") {
    assert(Set.empty.size == 0)
  }

  test("Invoking head on an empty Set should produce NoSuchElementException") {
    assertThrows[NoSuchElementException] {
      Set.empty.head
    }
  }
}

FlatSpec

A good first step for teams wishing to move from xUnit to BDD, FlatSpec's structure is flat like xUnit, 

so simple and familiar, 

but the test names must be written in a specification style: "X should Y," "A must B," etc.

import org.scalatest.FlatSpec

class SetSpec extends FlatSpec {

  "An empty Set" should "have size 0" in {
    assert(Set.empty.size == 0)
  }

  it should "produce NoSuchElementException when head is invoked" in {
    assertThrows[NoSuchElementException] {
      Set.empty.head
    }
  }
}

FunSpec

For teams coming from Ruby's RSpec tool, FunSpec will feel very familiar; More generally, for any team that prefers BDD, 

FunSpec's nesting and gentle guide to structuring text (with describe and it

provides an excellent general-purpose choice for writing specification-style tests.

import org.scalatest.FunSpec

class SetSpec extends FunSpec {

  describe("A Set") {
    describe("when empty") {
      it("should have size 0") {
        assert(Set.empty.size == 0)
      }

      it("should produce NoSuchElementException when head is invoked") {
        assertThrows[NoSuchElementException] {
          Set.empty.head
        }
      }
    }
  }
}

WordSpec

For teams coming from specs or specs2, WordSpec will feel familiar, and is often the most natural way to port specsN tests to ScalaTest. 

WordSpec is very prescriptive in how text must be written, so a good fit for teams who want a high degree of discipline enforced upon their

specification text.

import org.scalatest.WordSpec

class SetSpec extends WordSpec {

  "A Set" when {
    "empty" should {
      "have size 0" in {
        assert(Set.empty.size == 0)
      }

      "produce NoSuchElementException when head is invoked" in {
        assertThrows[NoSuchElementException] {
          Set.empty.head
        }
      }
    }
  }
}

FreeSpec

Because it gives absolute freedom (and no guidance) on how specification text should be written, 

FreeSpec is a good choice for teams experienced with BDD and able to agree on how to structure the specification text.

import org.scalatest.FreeSpec

class SetSpec extends FreeSpec {

  "A Set" - {
    "when empty" - {
      "should have size 0" in {
        assert(Set.empty.size == 0)
      }

      "should produce NoSuchElementException when head is invoked" in {
        assertThrows[NoSuchElementException] {
          Set.empty.head
        }
      }
    }
  }
}

PropSpec

PropSpec is perfect for teams that want to write tests exclusively in terms of property checks;

 also a good choice for writing the occasional test matrix when a different style trait is chosen as the main unit testing style.

import org.scalatest._
import prop._
import scala.collection.immutable._

class SetSpec extends PropSpec with TableDrivenPropertyChecks with Matchers {

  val examples =
    Table(
      "set",
      BitSet.empty,
      HashSet.empty[Int],
      TreeSet.empty[Int]
    )

  property("an empty Set should have size 0") {
    forAll(examples) { set =>
      set.size should be (0)
    }
  }

  property("invoking head on an empty set should produce NoSuchElementException") {
    forAll(examples) { set =>
       a [NoSuchElementException] should be thrownBy { set.head }
    }
  }
}

FeatureSpec

Trait FeatureSpec is primarily intended for acceptance testing, including facilitating the process of programmers working 

alongside non-programmers to define the acceptance requirements.

import org.scalatest._

class TVSet {
  private var on: Boolean = false
  def isOn: Boolean = on
  def pressPowerButton() {
    on = !on
  }
}

class TVSetSpec extends FeatureSpec with GivenWhenThen {

  info("As a TV set owner")
  info("I want to be able to turn the TV on and off")
  info("So I can watch TV when I want")
  info("And save energy when I'm not watching TV")

  feature("TV power button") {
    scenario("User presses power button when TV is off") {

      Given("a TV set that is switched off")
      val tv = new TVSet
      assert(!tv.isOn)

      When("the power button is pressed")
      tv.pressPowerButton()

      Then("the TV should switch on")
      assert(tv.isOn)
    }

    scenario("User presses power button when TV is on") {

      Given("a TV set that is switched on")
      val tv = new TVSet
      tv.pressPowerButton()
      assert(tv.isOn)

      When("the power button is pressed")
      tv.pressPowerButton()

      Then("the TV should switch off")
      assert(!tv.isOn)
    }
  }
}

RefSpec (JVM only)

RefSpec allows you to define tests as methods, which saves one function literal per test compared to style classes that represent tests as functions. 

Fewer function literals translates into faster compile times and fewer generated class files, which can help minimize build times. 

As a result, using Spec can be a good choice in large projects where build times are a concern as well as when 

generating large numbers of tests programatically via static code generators.

import org.scalatest.refspec.RefSpec

class SetSpec extends RefSpec {

  object `A Set` {
    object `when empty` {
      def `should have size 0` {
        assert(Set.empty.size == 0)
      }

      def `should produce NoSuchElementException when head is invoked` {
        assertThrows[NoSuchElementException] {
          Set.empty.head
        }
      }
    }
  }
}

Note: The "Ref" in RefSpec stands for reflection, which RefSpec uses to discover tests. As reflection is not availble in Scala.js, 

this class is not available on Scala.js.