G'day:
One thing I did not look at in any of my examinations of Kotest, and then JUnit5 was how to have data-driven tests in each platform. I'm going to start with how I'd've historically approached this task in a coupla frameworks I've used in the past.
TestBox
This is so easy to do in CFML I have not bothered to find out if TestBox has a native / idiomatic way of doing this.
describe("some tests", () => {
numbers = {
"one" = "tahi",
"two" = "rua",
"three" = "toru",
"four" = "wha"
}
testCases = [
{input="one", expected="tahi"},
{input="two", expected="rua"},
{input="three", expected="toru"},
{input="four", expected="wha"}
]
testCases.each((testCase) => {
it("should return #testCase.expected# when passed #testCase.input#", () => {
expect(numbers[testCase.input]).toBe(testCase.expected)
})
})
})
I loop over an array of cases, calling it with each variant.
PHPUnit
PHPUnit has a slightly clunkier approach, but gets there:
class DataProviderTest extends TestCase
{
public function setUp() : void
{
$this->numbers = [
"one" => "tahi",
"two" => "rua",
"three" => "toru",
"four" => "wha"
];
}
/** @dataProvider provideCasesForNumberMapperTests */
public function testNumberMapper($input, $expected)
{
$this->assertEquals($this->numbers[$input], $expected);
}
public function provideCasesForNumberMapperTests()
{
return [
["input" => "one", "expected" => "tahi"],
["input" => "two", "expected" => "rua"],
["input" => "three", "expected" => "toru"],
["input" => "four", "expected" => "wha"]
];
}
}
Same principle, except the iteration over the test cases specified in the data provider is handled internally by PHPUnit.
As an aside, I am pretty pleased with a small addition to the test output that PHPUnt has at the moment:
adam@DESKTOP-QV1A45U:/mnt/c/temp/phpunit_test$ vendor/bin/phpunit PHPUnit 9.5.25 #StandWithUkraine .... 4 / 4 (100%) Time: 00:00.100, Memory: 6.00 MB OK (4 tests, 4 assertions)
Kotest (Data Driven Testing)
Kotest is better than PHPUnit, but isn't as straight-forward as TestBox:
class DataDrivenTest : DescribeSpec({
describe("Data-driven tests") {
val numbers = mapOf(
Pair("one", "tahi"),
Pair("two", "rua"),
Pair("three", "toru"),
Pair("four", "wha")
)
data class TestCase(val input: String, val expected: String)
withData(
TestCase("one", "tahi"),
TestCase("two", "rua"),
TestCase("three", "toru"),
TestCase("four", "wha")
) { (input, expected) -> numbers[input] shouldBe expected }
}
})
It's pretty compact though. Here we need to add that data class (I have not looked at the difference between a "data class" and a "class that just has properties" yet: I had better). The iteration over the test data is intrinsic to the withData function, which takes a lambda that receives the test data unpacked as separate values, and is the actual test.
When these are run, they show as individual cases in the runner output (ie: within IntelliJ):
And in the HTML test report:
That's pretty clear.
JUnit (JUnit 5 User Guide › 2.18. Dynamic Tests)
This is pretty easy too (I was expecting some clunky Java-esque monster here, but no):
class DataDrivenTest {
private val numbers = mapOf(
Pair("one", "tahi"),
Pair("two", "rua"),
Pair("three", "toru"),
Pair("four", "wha")
)
@TestFactory
fun `Data-driven tests`() = listOf(
"one" to "tahi",
"two" to "rua",
"three" to "toru",
"four" to "wha"
).map { (input, expected) ->
DynamicTest.dynamicTest("numbers[$input] should be $expected") {
numbers[input] shouldBe expected
}
}
}
This is pretty similar to TestBox really. One needs that @TestFactory annotation to identify the function as - pretty much - a data provider, then one maps that as dynamicTest calls, which take a label and the lambda for the test (both of which have the data availed to them).
The test output is a bit clearer in this case, as we get to specify the specific test case label.
In IntelliJ:
And HTML test report:
All in all I'm pretty happy with both approaches here - Kotest's and JUnit's. I have to say I think I prefer the JUnit approach in this case. There's not much in it, that said.
The code from this article is at /src/test/kotlin/kotest/system/kotest/DataDrivenTest.kt and /src/test/kotlin/junit/system/junit/DataDrivenTest.kt. I have to concede I did not bother to save the CFML or PHP code. Ooops.
Righto.
--
Adam