使用ScalaCheck forAll,如何设置case class的一个参数,让其余的任意生成?

With ScalaCheck forAll, how do I set one parameter of case class and let the rest be arbitrarily generated?

所以我正在使用 ScalaTest/ScalaCheck 创建 Scala UnitTest 用于本地缓存实现,我需要测试功能,如果事件带有我们已经看到的 ID,它会更新该值在缓存中创建一个全新的条目。

我现在确定如何执行此操作以便为循环的每次迭代生成相同的 ID。 请参阅下面的代码。

forAll { arbitraryEvent: ClientFacingExecution =>
      val unmatchedEvent = ClientFacingExecutionEnriched(arbitraryFoExec)
      val expectedId = unmatchedEvent.clientFacingExecution.id

      ........................................
      ........................................
      ........................................

      cacheUnderTest.records should have size 1
}

如何确保 expectedId 始终相同,同时重新生成其余的 unmatchedEvent。

由于我不知道您的 class ClientFacingExecution 的完整定义,考虑一个类似的案例 class 记录定义为

case class Record(id: Int, str: String)

为案例class创建生成器,例如具有固定 id 的 Record,例如,

val someId = 1
val simpleRecordGenerator:Gen[Record] = Gen.resultOf(Record).map(_.copy(id = someId))

这种方法将确保 id 是固定的并且可能对您有用,但是如果您想生成 class 的其他字段,它缺乏灵活性。一种稍微复杂和灵活的方法允许您以新的方式组合生成器。

val someId = 1
val recordGenerator:Gen[Record] = Gen.zip(Gen.resultOf(Record), Gen.const(someId)).map{ case (record, id) => record.copy(id = id) }

forall(recordGenerator){ record: Record => 
...
cacheUnderTest.records should have size 1
} 

这里我们提供生成器作为 forAll 的第一个参数。 forAll 的第二个参数是一个函数,它接受作为第一个参数提供的生成器生成的值。

zip 方法,有时称为组合器,用于组合生成器。在这种情况下,我们为任意记录组合一个生成器,其中 id 可以是从 Integer.MIN_VALUE 到 Integer.MAX_VALUE 的任何值,然后用 Gen.const 覆盖该值是一个更受约束的值。这种结构非常灵活,如果您选择不大幅更改代码,您可以使用其他方式来限制值,例如使用 Gen.choose(0,100) 代替 Gen.const 来生成范围内的任意 id从 0 到 100。