CLOVIS
01/14/2025, 5:20 PMCLOVIS
01/14/2025, 5:20 PMclass SchemaTest : PreparedSpec({
// ...
})
that I will thus leave out for the other snippetsCLOVIS
01/14/2025, 5:22 PMclass SchemaTest : FunSpec({
beforeSpec {
validate = Ajv(Options(strict = true)).compile(
JSON.parse(readFile(schemaFile, utf8))
)
}
withData(readdir(dataDir).asIterable()) {
readFile(path.join(dataDir, it), utf8) should beValid()
}
})
lateinit var validate: ValidateFunction
fun beValid(): Matcher<String> {
return Matcher { data ->
MatcherResult(
validate(YAML.parse(data)),
{ "..." },
{ "..." },
)
}
}
Where readdir is the "problematic" call that is not working as not in a suspending context.CLOVIS
01/14/2025, 5:25 PMAjv
object. I don't know what that is, so I'll assume it's part of the SUT.
• then, we call readdir
which returns an iterable. I'll assume it's listing the files that are part of the directory somehow.
• for each of these, we want to execute a test that reads a file and uses a Kotest-style matcherCLOVIS
01/14/2025, 5:26 PMbeValid
function in any way. We can make it a top-level function and not have to touch anything else.CLOVIS
01/14/2025, 5:31 PMval validator by prepared {
Ajv(Options(strict = true)).compile(...)
}
test("Validate all") {
val files = readdir(dataDir).asIterable()
for (file in files) {
readFile(...) should beValid()
}
}
That works, but it uses a single test, so there's not much info if one of them fails. We'd prefer having a test per case.CLOVIS
01/14/2025, 5:31 PMCLOVIS
01/14/2025, 5:35 PMsuspend
to know which files exist. That's a problem because we can't suspend directly in suites (we want test discovery to be deterministic and immediate for purposes of IDE integration in the future)CLOVIS
01/14/2025, 5:38 PMbeforeSpec
is initialized before Kotest knows what the data is going to be. Honestly I'm surprised this even works, since tests are registered in a push-fashion internally. The same trick won't work with :runner-kotest because all tests are registered to Kotest before anything else happens.CLOVIS
01/14/2025, 5:39 PMCLOVIS
01/14/2025, 5:41 PMCLOVIS
01/14/2025, 5:46 PMreaddir
in a first class and access its result in a second test class that executes later, but I can't say i like thatVampire
01/14/2025, 5:47 PMbeforeSpec
that is not relevant for the topic.
It just initializes a variable once (Ajv which is a JSON schema validator) that is then used in the matcher.
The essence is, how to produce multiple test leafs from the result of a suspending call.
I would prefer something that uses kotest as it is going to be a contribution to a project using kotest already, but if it does not work with kotest but with something else that would also be ok for me.
I assume there really is no way to get the list of files without suspension?Only if you tell me how to list a directory without suspending. Besides that I can of course do it in Gradle and give the list of files to the test task, that is my fallback plan. But I'd prefer something inline.
I'm curious what the overall use-case isI intend to test a JSON schema against a directory of valid JSON files and a direcotry of invalid JSON files to make sure the schema is doing what it is intended to do also in the future. And I want to do that using Ajv as that is also going to be used later in production and known to work as expected. So I just throw in all valid files in one directory and all invalid files into another, without the need to add a test case for each manually as any manual action is prone to be forgotten or did wrongly, especially if it could have been automated.
Vampire
01/14/2025, 5:49 PMbeforeSpec
then I can just use normal withData
, as the result of that call will not change so it does not have to be right before the test but in beforeSpec
is fine.
🙈CLOVIS
01/14/2025, 5:50 PMCLOVIS
01/14/2025, 5:51 PMCLOVIS
01/14/2025, 5:51 PMVampire
01/14/2025, 6:13 PMbeforeSpec
is done after the argument for withData
is necessary 😕Vampire
01/14/2025, 6:23 PMVampire
01/14/2025, 6:32 PMreaddirSync
instead of readdir
as that is not suspending but blocking.
The general question is still interesting, but my use-case is solved with that.
🙈