应用于实现 Sub-类 的 Scala 特征测试

Scala Testing of Trait Applied to Implementing Sub-Classes

鉴于实现子类

的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())

如果 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())
  }

}

属性-based 测试可以完全满足您的需求。下面是一个使用 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 函数接受一个生成器,对生成器的值进行采样并对所有样本运行测试。我们的生成器将始终 return BarBaz 的实例,这意味着 属性 将涵盖您要测试的所有情况。 forAll 断言,如果单个测试失败,则整个 属性 都会失败。