给出了实现子类的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.doesStuff
和Baz.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
函数接受一个生成器,对发生器的值进行采样并对所有样本运行测试。我们的生成器将始终返回Bar
或Baz
的实例,这意味着该属性将涵盖您要测试的所有案例。 forAll
断言,如果单个测试失败,整个属性都会失败。