The power-assert compatible assertions DSL and some other testing goodies - a Kotlin multiplatform testing library.
I am mostly using kotest library for writing test assertions in my projects. When power-assert became the official Kotlin compiler plugin, I also realized that most of the kotest assertions can be replaced with something which suits my needs much better. Instead of writing:
x shouldBeGreaterThanOrEqualTo 42
I could write:
assert(x >= 42)
And the power-assert will provide comprehensive error message breaking down the expression written in Kotlin into components, while displaying their values.
I am quite often asserting the state of hierarchical data structures, therefore I came up with such a syntax, following the natural language, and utilizing power-assert:
message should {
have(id == 42)
have(content.size == 2)
content[0] should {
be<Text>()
have(type == "text")
have("Hello" in text)
}
content[1] should {
be<Image>()
have(type == "image")
have(width >= 800)
have(height >= 600)
mediaType should {
have(type == "image/png")
}
}
}
Now, if the image mediaType.type
is not PNG
, it will show:
Message(id=42, content=[Text(text=Hello there), Image(path=image.png, width=1024, height=768, mediaType=MediaType(type=image/jpeg))])
containing:
Image(path=image.png, width=1024, height=768, mediaType=MediaType(type=image/jpeg))
containing:
MediaType(type=image/jpeg)
should:
have(type == "image/png")
| |
| false
image/jpeg
In addition, the library supports:
- uniform access to project test files across non-browser platforms of a KMP project
- access to defined set of environment variables in browser platforms of a KMP project
In your build.gradle.kts
:
plugins {
kotlin("multiplatform") version "2.1.0"
kotlin("plugin.power-assert") version "2.1.0" // replace with the latest kotlin version
}
kotlin {
sourceSets {
commonTest {
depencencies {
implementation("com.xemantic.kotlin:xemantic-kotlin-test:1.3")
}
}
}
}
powerAssert {
functions = listOf(
"com.xemantic.kotlin.test.assert",
"com.xemantic.kotlin.test.have"
)
}
In your build.gradle.kts
:
plugins {
kotlin("jvm") version "2.1.0"
kotlin("plugin.power-assert") version "2.1.0" // replace with the latest kotlin version
}
dependencies {
testImplementation("com.xemantic.kotlin:xemantic-kotlin-test:1.3")
}
powerAssert {
functions = listOf(
"com.xemantic.kotlin.test.assert",
"com.xemantic.kotlin.test.have"
)
}
assert(2 + 2 == 4)
Note
The assert function in Kotlin stdlib is providing assert
only for jvm
and native
out of all the Kotlin multiplatform targets.
The multiplatform assert
function can be imported from com.xemantic.kotlin.test.assert
.
The library introduces should infix function, which allows you to chain assertions on an object:
someObject should {
// assertions go here
}
You can assert the type of object using the be function:
someObject should {
be<ExpectedType>()
}
Tip
After calling be
function with expected type, all the subsequent calls within should {}
will have access to the properties of the expected type, like if this
, representing someObject
, was cast to the expected type.
Use the have
function to assert conditions:
someObject should {
have(someProperty == expectedValue)
}
You can nest assertions for complex objects:
complexObject should {
have(property1 == expectedValue1)
nestedObject should {
have(nestedProperty == expectedValue2)
}
}
You can obtain access to the test context like:
- Stable absolute path of the current gradle root dir, so that the test files can be used in tests of non-browser platforms of the KMP project.
- Environment variables, accessible on almost all the platforms, including access to predefined set of environment variables in tests of browser platforms (e.g. API keys) of the KMP project.
See TextContext for details.
You have to add to build.gradle.kts
:
val gradleRootDir: String = rootDir.absolutePath
val fooValue = "bar"
tasks.withType<KotlinJvmTest>().configureEach {
environment("GRADLE_ROOT_DIR", gradleRootDir)
environment("FOO", fooValue)
}
tasks.withType<KotlinJsTest>().configureEach {
environment("GRADLE_ROOT_DIR", gradleRootDir)
environment("FOO", fooValue)
}
tasks.withType<KotlinNativeTest>().configureEach {
environment("GRADLE_ROOT_DIR", gradleRootDir)
environment("SIMCTL_CHILD_GRADLE_ROOT_DIR", gradleRootDir)
environment("FOO", fooValue)
environment("SIMCTL_CHILD_FOO", fooValue)
}
and specify environment variables you are interested in. The SIMCTL_CHILD_
is used in tests running inside emulators.
To pass environment variables to browser tests, you have to create webpack.confg.d
folder and drop this file named env-config.js
:
const webpack = require("webpack");
const envPlugin = new webpack.DefinePlugin({
'process': {
'env': {
'FOO': JSON.stringify(process.env.FOO)
}
}
});
config.plugins.push(envPlugin);
Pick environment variables which should be provided to browser tests.
Then you can write test like:
class FooTest {
@Test
fun `Should test against test data`() {
if (isBrowserPlatform) return // we don't have access to Gradle root dir
val testData = Path(gradleRootDir, "test-data.txt")
// ...
}
@Test
fun `Should use predefined environment variable`() {
val apiKey = getEnv("SOME_API_KEY")
// ...
}
}
Clone this project, and then run:
./gradlew build