mitch
09/18/2021, 12:05 AMtest(...) {
checkAll(arbFirst, arbSecond) { first, second ->
sut.execute(first, second)
}
}
and after
test(...) { sut.execute(arbFirst.value(), arbSecond.value()) }
It’s really mindblowing! I can imagine how many hundreds lines of code I can simplify in our current tests if kotest has this.mitch
09/18/2021, 12:12 AMmitch
09/18/2021, 12:13 AMsam
09/18/2021, 12:23 AMmitch
09/18/2021, 12:36 AMmitch
09/18/2021, 12:37 AM// if we'd need additional setups - which alot of time is possible
test("should test using arbs with setup") {
checkAll(arbFirst, arbSecond) { first, second
mongo.createDocument(first.copy(id = null))
val testFirst = mongo.findByName(first.name)
val testSecond = second.copy(id = testFirst.id)
systemUnderTest.execute(testFirst, testSecond)
}
}
I think this is possible
test("should test using arbs with setup") {
val first = mongo.createDocument(arbFirst.value().copy(id = null))
systemUnderTest.execute(
mongo.findByName(first.name),
arbSecond.value().copy(id = first.id)
)
}
mitch
09/18/2021, 12:40 AMmitch
09/18/2021, 12:41 AMmitch
09/18/2021, 12:48 AMmitch
09/18/2021, 12:53 AMtest("arbitraries should be used and registered") {
propertyContext(proptestConfig, ...etc) { // produces a random source, this is PropertyContext
val first = arbFirst.value() // calls arbFirst.generate(rs) under the hood and record the sample & shrinks.
val second = arbSecond.value() // calls arbSecond.generate(rs) under the hood and record the sample & shrinks.
systemUnderTest.execute(first, second)
}
}
// or simply the following, with Sam's property syntax
context("given some context") {
configureProperty(proptestConfig)
proptest("arbitraries should be used and registered") {
systemUnderTest.execute(arbFirst.value(), arbSecond.value())
}
}
sam
09/18/2021, 1:35 AMsam
09/18/2021, 1:35 AMsam
09/18/2021, 1:39 AMcheckAll {
val first = <http://Arb.int|Arb.int>().value()
val second = Arb.string().value()
sut.execute(first, second)
}
But isn't that just
checkAll<Int, String> { first, second ->
sut.execute(first, second)
}
Which is actually less code ?mitch
09/18/2021, 2:33 AMsam
09/20/2021, 12:44 AMcheckAll(iterations, [seed]) {
val first = <http://Arb.int|Arb.int>().value()
val second = Arb.string().value()
sut.execute(first, second)
}
And for anything other than that:
checkAll(config) {
val first = <http://Arb.int|Arb.int>().value()
val second = Arb.string().value()
sut.execute(first, second)
}
sam
09/20/2021, 12:44 AMsam
09/20/2021, 12:45 AMtest("all strings should be commutative under addition") {
checkAll(1000) {
val a = Arb.string().value
val b = Arb.string().value
a + b shouldBe b + a
}
}
vs
checkAll("all strings should be commutative under addition", 1000) {
val a = Arb.string().value
val b = Arb.string().value
a + b shouldBe b + a
}
mitch
09/20/2021, 12:47 AMcheckAll
like in your first example. Because all infrastructure are already set in there.sam
09/20/2021, 12:49 AMmitch
09/20/2021, 12:49 AMtest("...") {
val a = Arb.string().value()
val b = Arb.string().value()
a + b shouldBe b + a
}
which kinda suggest we need to be propagate some base PropertyConfig in the CoroutineContextmitch
09/20/2021, 12:50 AMsam
09/20/2021, 12:50 AMsam
09/20/2021, 12:50 AMmitch
09/20/2021, 12:51 AMmitch
09/20/2021, 12:52 AMtest("...") {
val a = Arb.string().value() // that's new, so everytime it loops, you get a new arb
val b = Arb.string().value()
a + b shouldBe b + a
}
sam
09/20/2021, 12:52 AMsam
09/20/2021, 12:52 AMsam
09/20/2021, 12:52 AMmitch
09/20/2021, 12:55 AMmitch
09/20/2021, 12:58 AMclassifications
mutable map?sam
09/20/2021, 12:58 AMmitch
09/20/2021, 12:58 AMsam
09/20/2021, 12:59 AMmitch
09/20/2021, 1:01 AMsam
09/20/2021, 1:01 AMmitch
09/20/2021, 1:01 AMmitch
09/20/2021, 1:01 AMsam
09/20/2021, 1:01 AMmitch
09/20/2021, 1:02 AMfun autoclassifications(): Map<String, Map<String, Int>> = autoclassifications.toMap()
sam
09/20/2021, 1:03 AMsam
09/20/2021, 1:03 AMmitch
09/20/2021, 1:03 AMsam
09/20/2021, 1:03 AMmitch
09/20/2021, 1:07 AMinternal suspend fun test(
context: PropertyContext,
config: PropTestConfig,
shrinkfn: suspend () -> List<ShrinkResult<Any?>>,
inputs: List<Any?>,
classifiers: List<Classifier<out Any?>?>, // this
seed: Long,
fn: suspend () -> Any
) {
require(inputs.size == classifiers.size)
try {
inputs.indices.forEach { k ->
val value = inputs[k]
val classifier = classifiers[k]
if (classifier != null) {
val label = (classifier as Classifier<Any?>).classify(value) // this
if (label != null) context.classify(k, label)
}
}
}
sam
09/20/2021, 1:08 AMsam
09/20/2021, 1:09 AMsam
09/20/2021, 1:09 AMmitch
09/20/2021, 1:11 AMsam
09/20/2021, 1:11 AMsam
09/20/2021, 1:11 AMsam
09/20/2021, 1:11 AMsam
09/20/2021, 1:11 AMsam
09/20/2021, 1:12 AMsam
09/20/2021, 1:12 AMmitch
09/20/2021, 1:16 AMsealed class Gen<A> {
suspend fun TestContext.value(): A = getOrCreatePropertyContext { ctx ->
val a = next(rs)
ctx.store(a)
a
}
}
sam
09/20/2021, 1:17 AMsam
09/20/2021, 1:17 AMmitch
09/20/2021, 1:17 AMtest("stuff") { // new context here
}
mitch
09/20/2021, 1:18 AMsam
09/20/2021, 1:18 AMsam
09/20/2021, 1:18 AMmitch
09/20/2021, 1:19 AMinterface TestContext : PropertyContext, CoroutineScope { ...
}
mitch
09/20/2021, 1:19 AMsam
09/20/2021, 1:19 AMmitch
09/20/2021, 1:20 AMmitch
09/20/2021, 1:21 AMinterface DynamicPropertyContext : PropertyContext {
fun <A> Arb<A>.value(): A { ... }
}
sam
09/20/2021, 1:22 AMsam
09/20/2021, 1:22 AMsam
09/20/2021, 1:23 AMPropertyContext
is not an interface so you can add to it safelymitch
09/20/2021, 1:23 AMsam
09/20/2021, 1:23 AMmitch
09/20/2021, 1:23 AMsam
09/20/2021, 1:29 AMsam
09/20/2021, 1:29 AMmitch
09/20/2021, 1:30 AMmitch
09/20/2021, 1:30 AMsam
09/20/2021, 1:30 AMmitch
09/20/2021, 1:30 AMsam
09/20/2021, 1:36 AMtest("checkAll should setup a property context") {
var count = 0
checkAll(100) {
val a = Arb.string().value()
val b = Arb.string().value()
(a + b).length shouldBe b.length + a.length
count++
}
count shouldBe 100
}
test("checkAll should use a repeatable seed") {
checkAll(1, 52104482139021L) {
val a = Arb.string().value()
a shouldBe "cUj%x=.f6ktw\"icenM(AEw&5CP3q+8FxU*xi<p!Jbd"
}
}
sam
09/20/2021, 1:37 AMmitch
09/20/2021, 1:37 AMsam
09/20/2021, 1:39 AMsam
09/20/2021, 1:41 AMtest("auto labelling") {
val context = checkAll(100, 98173L) {
Arb.string().value()
}
context.autoclassifications()["1"] shouldBe mapOf(
"MAX LENGTH" to 3,
"ANY LENGTH LETTER OR DIGITS" to 3
)
}
sam
09/20/2021, 1:41 AMsam
09/20/2021, 1:41 AMsam
09/20/2021, 1:44 AMsam
09/20/2021, 1:44 AMsam
09/20/2021, 1:46 AMmitch
09/20/2021, 3:57 AM