匹配器
简单匹配器
val list = 2::4::5::Nil
list.size should be (3)
字符串匹配器
val str = """I fell into a burning ring of fire. I went down, down, down and the flames went higher"""
string should startWith("I fell")
string should endWith("higher")
string should not endWith "My favorite friend, the end"
string should include("down, down, down")
string should not include ("Great balls of fire")
string should startWith regex ("I.fel+")
string should endWith regex ("h.{4}r")
string should not endWith regex("\\d{5}")
string should include regex ("flames?")
string should fullyMatch regex ("""I(.|\n|\S)*higher""")
关系操作匹配器
val answerToLife = 42
answerToLife should be < (50)
answerToLife should not be > (50)
answerToLife should be > (3)
answerToLife should be <= (100)
answerToLife should be >= (0)
answerToLife should be === (42)
answerToLife should not be === (400)
ScalaTest要求相等用===操作,或者should be,或者should equal
浮点数匹配器
在计算机中,采用的是2进制,10进制的加减法不一定能得到精确值。故测试时应注意加减一个误差。
(0.9 - 0.8) should be (0.1 plusOrMinus .01)
(0.4 + 0.1) should not be (40.00 plusOrMinus .30)
引用匹配器
val garthBrooks = new Artist("Garth", "Brooks")
val chrisGaines = garthBrooks
garthBrooks should be theSameInstanceAs (chrisGaines)
val debbieHarry = new Artist("Debbie", "Harry")
garthBrooks should not be theSameInstanceAs(debbieHarry)
Iterable匹配器
List() should be('empty)
8 :: 6 :: 7 :: 5 :: 3 :: 0 :: 9 :: Nil should contain(7)
Seq和traversable匹配器
(1 to 9) should have length (9)
(20 to 60 by 2) should have size (21)
Map匹配器
val map = Map("Jimmy Page" -> "Led Zeppelin", "Sting" -> "The Police",
"Aimee Mann" -> "Til\' Tuesday")
map should contain key ("Sting")
map should contain value ("Led Zeppelin")
map should not contain key("Brian May")
复合操作匹配器
val redHotChiliPeppers = List("Anthony Kiedis", "Flea", "Chad Smith", "Josh
Klinghoffer")
redHotChiliPeppers should (contain("Anthony Kiedis") and
(not contain ("John Frusciante")
or contain("Dave Navarro")))
其中
redHotChiliPeppers should not contain "The Edge" or contain "Kenny G"
和
redHotChiliPeppers should not (contain "The Edge" or contain "Kenny G")
是一样的。
scalaTest不会短路
var total = 3
redHotChiliPeppers should not (contain ("The Edge") or contain {total += 6;
"Kenny G"})
total should be (9)
即便是redHotChiliPeppers确实不含有The Edge,后面的total += 6照样会执行,不会短路。
由于scala中有Option,scala中不会有null,但是scala与java互操作,可能会出现null,为了避免空指针异常,可以将
gorillaz should (not be (null) and contain ("Damon Albarn"))
改为
gorillaz should not be (null)
gorillaz should contain ("Damon Albarn")
属性匹配器
import scala.collection.mutable.WrappedArray
val album = new Album("Blizzard of Ozz", 1980, new Artist("Ozzy", "Osbourne"))
album should have (
'title ("Blizzard of Ozz"),
'year (1980),
'artist (new Artist("Ozzy", "Osbourne"))
)
java.util.Collection匹配器
import java.util.{List => JList, ArrayList => JArrayList, Map => JMap,
HashMap => JHashMap}
val jList: JList[Int] = new JArrayList[Int](20)
jList.add(3); jList.add(6); jList.add(9)
val emptyJList: JList[Int] = new JArrayList[Int]()
emptyJList should be('empty)
jList should have length (3)
jList should have size (3)
jList should contain(6)
jList should not contain (10)
val backupBands: JMap[String, String] = new JHashMap()
backupBands.put("Joan Jett", "Blackhearts")
backupBands.put("Tom Petty", "Heartbreakers")
backupBands should contain key ("Joan Jett")
backupBands should contain value ("Heartbreakers")
backupBands should not contain key("John Lydon")
异常捕获
“An album” should {
“throw an IllegalArgumentException if there are no acts when created” in {
intercept[IllegalArgumentException] {
new Album(“The Joy of Listening to Nothing”, 1980, List())
}
}
}
用evaluating块来不会期望的异常
val thrownException = evaluating {new Album("The Joy of Listening to Nothing", 1980, List())} must produce [IllegalArgumentException]
thrownException.getMessage() must be ("An Artist is required")
Informers
主要是为了打印日志,方便观察,类似于debug。
class AlbumSpec extends FunSpec with ShouldMatchers {
describe("An Album") {
it("can add an Artist to the album at construction time") {
val album = new Album("Thriller", 1981, new Artist("Michael", "Jackson"))
info("Making sure that Michael Jackson is indeed the artist of Thriller")
album.acts.head.asInstanceOf[Artist].firstName should be("Michael")
album.acts.head.asInstanceOf[Artist].lastName should be("Jackson")
}
}
}
GivenWhenThen
这个也是为了打印一些信息,以便观察哪里出问题了,类似于打印日志。
class AlbumSpec extends FunSpec with ShouldMatchers with GivenWhenThen {
describe("An Album") {
it("can add an Artist to the album at construction time") {
given("The album Thriller by Michael Jackson")
val album = new Album("Thriller", 1981, new Artist("Michael", "Jackson"))
when("the album\'s artist is obtained")
val artist = album.artist
then("the artist obtained should be an instance of Artist")
artist.isInstanceOf[Artist] should be (true)
and("the artist's first name and last name should be Michael Jackson")
artist.firstName should be("Michael")
artist.lastName should be("Jackson")
}
}
}
Pending测试
占位符,方便记下一个想法,但是还没有实现或者还没准备好。
class AlbumSpec extends FunSpec with ShouldMatchers with GivenWhenThen {
describe("An Album") {
it("can add an Artist to the album at construction time") {pending}
it("can add opt to not have any artists at construction time") {pending}
}
}
Ignoring测试
开发人员不确定测试的有效性,或者生产代码已经淘汰了,抑或生产代码太复杂,所以暂时需要忽略这些测试代码。
package com.oreilly.testingscala
import org.scalatest.FunSpec
import org.scalatest.matchers.ShouldMatchers
class AlbumSpec extends FunSpec with ShouldMatchers {
describe("An Album") {
//Code removed for brevity
ignore("can add a Producer to an album at construction time") {
new Album("Breezin\'", 1976, new Artist("George", "Benson"))
//TODO: Figure out the implementation of an album producer
}
}
}
Tagging
分类测试,好处
- 某些测试比较慢,你可能想略过
- 一些测试检查相关功能性,应该一起运行
- 你可能想将测试分为单元测试,集成测试,验收测试或者其他的类型
Specifications行为规范
FunSepc,WordSpec,FeatureSpec,FreeSpec,FlatSpec大同小异,这里以FeatureSpec为例
FeatureSpec是将测试分类为一系列的功能的测试。一个feature是软件的一个简单的功能点。每个功能将有该功能的多个场景(测试用例),每个场景代表一个成功的或者失败的测试案例。场景越多,测试越充分,健壮性越好。
package com.oreilly.testingscala
import org.scalatest.matchers.ShouldMatchers
import org.scalatest.FeatureSpec
class AlbumFeatureSpec extends FeatureSpec with ShouldMatchers {
feature("An album's default constructor should support a parameter that accepts
Option(List(Tracks)) ") { ... }
feature("An album should have an addTrack method that takes a track and returns
an immutable copy of the Album with the added track") { ... }
}