ScalaTest——Fixtures

xiaoxiao2022-06-11  25

Fixture翻译成中文有这么些意思:固定装置;卡具;固定附物,固定附着物;固定财产,在ScalaTest中,可能会有这么一种情境:在一个测试类中,不同的测试方法需要的类实例、依赖等数据是一样的,显然,没必要为每个测试类去new一些它们专用的数据,可以提供一些公共的数据,然后在不同的测试方法中重用它们。

要做到数据的重用,有很多方法:

Scala语言自带的方法ScalaTest测试框架提供的解决方案每一种测试方法也有自己的一些实现JUnit和TestNG也有它们自己的结构
8.1 匿名对象

先从Scala语言本身提供的方案说起。Scala语言提供的匿名对象可以用来解决前面说到的数据重用的问题。Scala中的匿名对象就是没有名字的对象,匿名对象一旦被创建,就可以在不同的测试方法中重用。每次匿名对象被请求的时候,它都会创建一个全新的对象。如下面的例子:

import org.scalatest.Matchers import org.scalatest.FunSpec class AlbumFixtureSpec extends FunSpec with Matchers { def fixture = new { val letterFromHome = new Album("Letter from Home", 1989, new Band("Pat Metheny Group")) } describe("The Letter From Home Album by Pat Metheny") { it("should get the year 1989 from the album") { val album = fixture.letterFromHome album.year should be (1989) } } }

上面的例子定义了一个fixture方法来获取一个Album对象,fixture方法每次被调用都会产生一个匿名对象。

这里有一点需要注意的,即使fixture方法产生的是一个可变mutable的对象,在另一个方法调用fixture时,它仍然后产生一个新的对象,而不是提供之前的对象。下面的例子使用了可变集合来说明:

import org.scalatest.FunSpec import org.scalatest.Matchers class AlbumMutableFixtureSpec extends FunSpec with Matchers { def fixture = new { import scala.collection.mutable._ val beachBoys = new Band("Beach Boys") val beachBoysDiscography = new ListBuffer[Album]() beachBoysDiscography += (new Album("Surfin' Safari", 1962, beachBoys)) } describe("Given a single fixture each beach boy discography initially contains a single album") { it("then after 1 album is added, the discography size should have 2") { val discographyDeBeachBoys = fixture.beachBoysDiscography discographyDeBeachBoys += (new Album("Surfin' Safari", 1962, fixture.beachBoys)) discographyDeBeachBoys.size should be(2) } it("then after 2 albums are added, the discography size should return 3") { val discographyDeBeachBoys = fixture.beachBoysDiscography discographyDeBeachBoys += (new Album("Surfin' Safari", 1962, fixture.beachBoys)) discographyDeBeachBoys += (new Album("All Summer Long", 1964, fixture.beachBoys)) discographyDeBeachBoys.size should be(3) } } }

跟前一个例子一样,上面的例子使用了fixture方法,在Scala语言中,使用def定义的方法在每次被调用的时候都会重新执行方法体。因而,在每个测试方法中我们得到的都是新的实例。

8.2 Fixture Traits

另一种在ScalaTest中的可供选择的做法是自定义一个特质来确保每个测试方法都得到不同的对象。特质在混入对象后仍然后持有它原来的方法,并不会在混入的对象之中共享。下面的例子使用一个特质而不是一个匿名对象:

import org.scalatest.Matchers import org.scalatest.FunSpec class AlbumFixtureTraitSpec extends FunSpec with Matchers { trait AlbumFixture { val letterFromHome = new Album("Letter from Home", 1989, new Band("Pat Metheny Group")) } describe("The Letter From Home Album by Pat Metheny") { it("should get the year 1989 from the album") { new AlbumFixture { letterFromHome.year should be(1989) } } } }

上面的例子使用了一个特质来封装测试方法需要的数据,在特质中又使用了匿名对象的方式来创建对象,实际上,这种实现方式依然使用了Scala的语言特性。

8.3 OneInstancePerTest

除了依赖Scala的语言特性,ScalaTest也提供了方法来确保每个测试都有它自己的数据实例。下面的例子使用了OnInstancePerTest特质来实现:

import org.scalatest.Matchers import collection.mutable.ListBuffer import org.scalatest.{FreeSpec, OneInstancePerTest} class AlbumListOneInstancePerTestFreeSpec extends FreeSpec with Matchers with OneInstancePerTest { val graceJonesDiscography = new ListBuffer[Album]() graceJonesDiscography += (new Album("Portfolio", 1977, new Artist("Grace", "Jones"))) "Given an initial Grace Jones Discography" - { "when an additional two albums are added, then the discography size should be 3" in { graceJonesDiscography += (new Album("Fame", 1978, new Artist("Grace", "Jones"))) graceJonesDiscography += (new Album("Muse", 1979, new Artist("Grace", "Jones"))) graceJonesDiscography.size should be(3) } "when one additional album is added, then the discography size should be 2" in { graceJonesDiscography += (new Album("Warm Leatherette", 1980, new Artist("Grace", "Jones"))) graceJonesDiscography.size should be(2) } } "Given an initial Grace Jones Discography " - { "when one additional album from 1980 is added, then the discography size should be 2" in { graceJonesDiscography += (new Album("Nightclubbing", 1981, new Artist("Grace", "Jones"))) graceJonesDiscography.size should be(2) } } }

上面的例子使用了FreeSpec风格的测试写法。在测试开始时,定义了graceJonesDiscography变量,然后该变量被用在多个测试中。由于AlbumListOneInstancePerTestFreeSpec类混入了OneInstancePerTest接口,graceJonesDiscography变量在每个测试方法中使用时都会被重新创建。

上面的例子中,测试方法是在in代码块中的内容。

8.4 Before and After

为了更好的控制在测试方法执行前、后有什么行为,ScalaTest提供了一个名为BeforeAndAfter的特质。可以很方便的指定在每一个测试方法执行前有什么行为,在每个测试方法执行后有什么行为。如下面的例子:

import collection.mutable.ListBuffer import org.scalatest.{BeforeAndAfter, WordSpec} import org.scalatest.Matchers class AlbumBeforeAndAfterFixtureSpec extends WordSpec with Matchers with BeforeAndAfter { val humanLeagueDiscography = new ListBuffer[Album]() before { info("Starting to populate the discography") humanLeagueDiscography += (new Album("Dare", 1981, new Band("Human League"))) } "A mutable ListBuffer of albums" should { "have a size of 3 when two more albums are added to the Human League Discography" in { humanLeagueDiscography += (new Album("Hysteria", 1984, new Band("Human League"))) humanLeagueDiscography += (new Album("Crash", 1986, new Band("Human League"))) humanLeagueDiscography should have size (3) } "have a size of 2 when one more album is added to the Human League Discography" in { humanLeagueDiscography += (new Album("Romantic", 1990, new Band("Human League"))) humanLeagueDiscography should have size (2) } } after { info("Clearing the discography") humanLeagueDiscography.clear() } }

上面的例子使用了WordSpec风格的测试,在测试方法执行前,初始化了一个名为humanLeagueDiscography的可变列表,在测试方法执行完毕后,humanLeagueDiscography可变列表被清空。BeforeAndAfter特质中的before和after方法和在JUnit中被标记为Before和After的方法作用是一样的。

上面的例子中,测试方法依然是在in代码块中的内容。

转载请注明原文地址: https://www.6miu.com/read-4930276.html

最新回复(0)