我想知道为什么需要Arbitrary,因为自动化属性测试需要属性定义,比如
val prop = forAll(v: T => check that property holds for v)
Run Code Online (Sandbox Code Playgroud)
和价值v发电机.用户指南说您可以为自定义类型创建自定义生成器(例如,树的生成器).然而,它并没有解释为什么你需要最重要的仲裁.
这是一本手册
implicit lazy val arbBool: Arbitrary[Boolean] = Arbitrary(oneOf(true, false))
Run Code Online (Sandbox Code Playgroud)
要获得对您自己的类型T的支持,您需要定义一个类型为Arbitrary [T]的隐式def或val.使用工厂方法Arbitrary(...)创建Arbitrary实例.此方法采用Gen [T]类型的一个参数并返回Arbitrary [T]的实例.
它清楚地表明我们需要在Gen之上的任意.但任意的理由并不令人满意
任意生成器是ScalaCheck在为属性参数生成值时使用的生成器.
IMO,要使用生成器,您需要导入它们而不是将它们包装到仲裁中!否则,我们可以争辩说我们需要将仲裁包装到其他东西中以使它们可用(等等无限期地无限地包装包装器).
您还可以解释如何arbitrary[Int]
将参数类型转换为生成器.这很奇怪,我觉得这些是相关的问题.
Ric*_*son 21
forAll { v: T => ... }
在Scala implicits的帮助下实现.这意味着可以T
隐式找到该类型的生成器,而不是由调用者明确指定.
Scala隐含很方便,但如果您不确定当前隐含值或转换的范围,它们也会很麻烦.通过使用特定类型(Arbitrary
)进行隐式查找,ScalaCheck尝试约束使用implicits的负面影响(这种使用也使其类似于某些用户熟悉的Haskell 类型类).
所以,你完全正确,Arbitrary
并不是真的需要.可以通过隐含Gen[T]
值实现相同的效果,可以说是更隐含的范围混淆.
作为最终用户,您应该将其Arbitrary[T]
视为该类型的默认生成器T
.您可以(通过作用域)定义和使用多个Arbitrary[T]
实例,但我不推荐它.相反,只需跳过Arbitrary
并明确指定您的生成器:
val myGen1: Gen[T] = ... val mygen2: Gen[T] = ... val prop1 = forAll(myGen1) { t => ... } val prop2 = forAll(myGen2) { t => ... }
arbitrary[Int]
就像forAll { n: Int => ... }
,它只是查找隐式Arbitrary[Int]
实例并使用它的生成器.实现很简单:
def arbitrary[T](implicit a: Arbitrary[T]): Gen[T] = a.arbitrary
这里的实施Arbitrary
可能也会有所帮助:
sealed abstract class Arbitrary[T] { val arbitrary: Gen[T] }
Bru*_*eth 10
ScalaCheck已从Haskell QuickCheck库中移植.在Haskell类型类中,只允许给定类型的一个实例,从而迫使您进入这种分离.但是在Scala中,没有这样的约束,可以简化库.我的猜测是,ScalaCheck(最初编写为)QuickCheck的1-1映射,使Haskellers更容易进入Scala :)
这是Haskell对任意的定义
class Arbitrary a where
-- | A generator for values of the given type.
arbitrary :: Gen a
Run Code Online (Sandbox Code Playgroud)
和Gen
newtype Gen a
Run Code Online (Sandbox Code Playgroud)
正如你所看到的,它们有一个非常不同的语义,Arbitrary是一个类型类,而Gen是一个带有一堆组合器的包装器来构建它们.
我同意"通过语义限制范围"的论点有点模糊,在组织代码时似乎没有被认真对待:任意类有时只是简单地委托给Gen实例.
/** Arbirtrary instance of Calendar */
implicit lazy val arbCalendar: Arbitrary[java.util.Calendar] =
Arbitrary(Gen.calendar)
Run Code Online (Sandbox Code Playgroud)
有时会定义自己的发电机
/** Arbitrary BigInt */
implicit lazy val arbBigInt: Arbitrary[BigInt] = {
val long: Gen[Long] =
Gen.choose(Long.MinValue, Long.MaxValue).map(x => if (x == 0) 1L else x)
val gen1: Gen[BigInt] = for { x <- long } yield BigInt(x)
/* ... */
Arbitrary(frequency((5, gen0), (5, gen1), (4, gen2), (3, gen3), (2, gen4)))
}
Run Code Online (Sandbox Code Playgroud)
所以实际上这会导致代码重复(每个默认的Gen都被一个任意的镜像)和一些混淆(为什么Arbitrary[BigInt]
不包装默认值Gen[BigInt]
?).