如何用ScalaCheck生成任意类型的任意函数测试compose方法?
如何用ScalaCheck泛型测试compose函数?
好消息是,用ScalaCheck做泛型属性测试完全可行!你只需要让ScalaCheck知道如何为泛型类型生成随机测试数据,以及如何比较结果的相等性。下面是具体的实现方案:
首先,先回顾你的compose函数:
def compose[A, B, C](g: B => C, f: A => B): A => C = f.andThen(g)
泛型测试的核心要求
ScalaCheck的forAll在处理泛型时需要两个关键的类型类支持:
Arbitrary[T]:负责生成类型T的随机测试实例,ScalaCheck已经为所有标准Scala类型(Int、String、Double等)提供了默认实现。Eq[T]:负责判断两个T类型的值是否相等,同样标准类型也有默认实现。
完整的泛型测试代码
你可以通过上下文边界(context bounds)来简洁地声明这些依赖,修改后的测试类如下:
import org.scalatest.prop.PropertyChecks import org.scalatest.{FlatSpec, Matchers} import org.scalacheck.Arbitrary._ import org.scalacheck.Eq._ class ComposeSpec extends FlatSpec with Matchers with PropertyChecks { // 你的compose函数,或者从其他地方导入 def compose[A, B, C](g: B => C, f: A => B): A => C = f.andThen(g) "The compose function" should "correctly combine two functions for any valid types" in { // 使用泛型forAll,通过上下文边界声明类型类依赖 forAll { [A: Arbitrary, B: Arbitrary, C: Arbitrary : Eq](a: A, g: B => C, f: A => B) => compose(g, f)(a) should equal(g(f(a))) } } }
代码解释
[A: Arbitrary]是(implicit arbA: Arbitrary[A])的语法糖,告诉Scala我们需要一个能生成A类型随机值的实例。[C: Eq]确保我们可以比较两个C类型的值是否相等,这是should equal(...)断言所需要的。- 函数类型(比如
A => B)的Arbitrary实例由ScalaCheck自动推导,只要A和B有对应的Arbitrary实例即可。
另一种写法:直接使用ScalaCheck的Prop
如果你更习惯纯ScalaCheck的风格,也可以直接创建一个Prop并调用check方法:
import org.scalacheck.Prop.forAll import org.scalatest.{FlatSpec, Matchers} class ComposeSpec extends FlatSpec with Matchers { def compose[A, B, C](g: B => C, f: A => B): A => C = f.andThen(g) "compose" should "obey the function composition law generically" in { val compositionLaw = forAll { [A: Arbitrary, B: Arbitrary, C: Arbitrary : Eq](a: A, g: B => C, f: A => B) => compose(g, f)(a) === g(f(a)) } compositionLaw.check } }
注意事项
- 如果你要测试自定义类型,需要为这些类型手动提供
Arbitrary和Eq实例(比如通过implicit val arbitraryMyType: Arbitrary[MyType] = ...)。 - 如果遇到类型推断问题,可以显式指定类型参数,比如
forAll[Int, String, Boolean](...),不过大多数情况下Scala会自动处理。
内容的提问来源于stack exchange,提问作者Marcus Vinícius Monteiro
相关产品推荐
相关产品推荐

