特质的Scala测试应用于实现子类

时间:2019-03-01 14:52:27

标签: scala scalatest

通过实现子类赋予scala特征

trait Foo {
  def doesStuff() : Int
}

case class Bar() extends Foo { ... }

case class Baz() extends Foo { ... }

如何组织单元测试以测试特征,然后将测试应用于每个实现?

我正在寻找某种形式的东西:

class FooSpec(foo : Foo) extends FlatSpec {
  "A Foo" should {
    "do stuff" in {
      assert(foo.doesStuff == 42)
    }
  }
}

然后将哪个应用于每个实现类:

FooSpec(Bar())

FooSpec(Baz())

1 个答案:

答案 0 :(得分:0)

如果Bar.doesStuffBaz.doesStuff的实现具有不同的行为,则具有两个单独的测试是合适的解决方案。

import org.scalatest.FlatSpec

class FooSpec1 extends FlatSpec {

  "a Bar" should "do a bar thing" in {
    Bar().doesStuff() == 42
  }

  "a Baz" should "do a baz thing" in {
    Baz().doesStuff() % 2 == 0
  }

}

但是,如果它们具有相同的行为,则可以使用一个函数来重构测试以避免重复的代码。我不认为scalatest可以像您所要求的那样在规范级别实现这种重用模式。

import org.scalatest.FlatSpec

class FooSpec2 extends FlatSpec {

  def checkDoesStuff(foo: Foo): Boolean =
    foo.doesStuff() == 42

  "a Bar" should "do a bar thing" in {
    checkDoesStuff(Bar())
  }

  "a Baz" should "do a baz thing" in {
    checkDoesStuff(Baz())
  }

}

基于属性的测试可以完全满足您的需求。这是使用scalacheck的示例:

import org.scalacheck.{Gen, Properties}
import org.scalacheck.Prop.forAll

object FooProperties extends Properties("Foo"){

  val fooGen: Gen[Foo] = Gen.pick(1, List(Bar(), Baz())).map(_.head)

  property("a Foo always does stuff") = forAll(fooGen){
    (foo: Foo) => foo.doesStuff() == 42
  }

}

与ScalaTest规范不同,属性始终是函数。 forAll函数使用一个生成器,对该生成器的值进行采样,并对所有采样进行测试。我们的生成器将始终返回BarBaz的实例,这意味着该属性将涵盖您要测试的所有情况。 forAll断言,如果单个测试失败,则整个属性都会失败。