通过实现子类赋予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())
答案 0 :(得分:0)
如果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
断言,如果单个测试失败,则整个属性都会失败。