Kotlin Power-assert 编译器插件通过提供带有上下文信息的详细失败消息, 改善调试的体验. 它通过在失败消息中自动生成中间值, 简化测试代码的编写过程. 它帮助你理解测试失败的原因, 而不需要使用复杂的断言库.
Incorrect length
assert(hello.length == world.substring(1, 4).length) { "Incorrect length" }
| | | | | |
| | | | | 3
| | | | orl
| | | world!
| | false
| 5
Hello
使用插件
本节提供一些使用 Power-assert 编译器插件的示例.
下面是所有这些示例的构建脚本文件 build.gradle.kts
的完整代码:
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
plugins {
kotlin("jvm") version "2.0.21"
kotlin("plugin.power-assert") version "2.0.21"
}
group = "org.example"
version = "1.0-SNAPSHOT"
repositories {
mavenCentral()
}
dependencies {
testImplementation(kotlin("test"))
}
tasks.test {
useJUnitPlatform()
}
@OptIn(ExperimentalKotlinGradlePluginApi::class)
powerAssert {
functions = listOf("kotlin.assert", "kotlin.test.assertEquals", "kotlin.test.assertTrue", "kotlin.test.assertNull", "kotlin.require", "org.example.AssertScope.assert")
}
Assert 函数
我们来看看下面的测试, 使用 assert()
函数:
import kotlin.test.Test
class SampleTest {
@Test
fun testFunction() {
val hello = "Hello"
val world = "world!"
assert(hello.length == world.substring(1, 4).length) { "Incorrect length" }
}
}
如果你启用 Power-assert 插件来运行 testFunction()
测试, 你会得到明确的失败消息:
Incorrect length
assert(hello.length == world.substring(1, 4).length) { "Incorrect length" }
| | | | | |
| | | | | 3
| | | | orl
| | | world!
| | false
| 5
Hello
要得到更加完整的错误消息, 一定要将变量内联到测试函数的参数中. 我们来看看下面的测试函数:
class ComplexExampleTest {
data class Person(val name: String, val age: Int)
@Test
fun testComplexAssertion() {
val person = Person("Alice", 10)
val isValidName = person.name.startsWith("A") && person.name.length > 3
val isValidAge = person.age in 21..28
assert(isValidName && isValidAge)
}
}
执行代码的输出不能提供足够的信息找出问题的原因:
Assertion failed
assert(isValidName && isValidAge)
| |
| false
true
下面将变量内联到 assert()
函数中:
class ComplexExampleTest {
data class Person(val name: String, val age: Int)
@Test
fun testComplexAssertion() {
val person = Person("Alice", 10)
assert(person.name.startsWith("A") && person.name.length > 3 && person.age > 20 && person.age < 29)
}
}
执行后, 你会得到关于错误的更加明确的信息:
Assertion failed
assert(person.name.startsWith("A") && person.name.length > 3 && person.age > 20 && person.age < 29)
| | | | | | | | | |
| | | | | | | | | false
| | | | | | | | 10
| | | | | | | Person(name=Alice, age=10)
| | | | | | true
| | | | | 5
| | | | Alice
| | | Person(name=Alice, age=10)
| | true
| Alice
Person(name=Alice, age=10)
除 assert 之外的其它函数
Power-assert 插件 默认转换 assert
, 但也能够转换各种其它函数. 例如 require()
, check()
, assertTrue()
, assertEqual()
以及其它函数, 都可以转换, 只要这些函数存在一种形式, 允许接受一个 String
或 () -> String
值, 作为最后一个参数.
在测试中使用新的函数之前, 要在你的构建脚本文件的 powerAssert {}
代码块中指定这个函数. 例如, 对 require()
函数:
// build.gradle.kts
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
@OptIn(ExperimentalKotlinGradlePluginApi::class)
powerAssert {
functions = listOf("kotlin.assert", "kotlin.require")
}
添加这个函数之后, 你可以在你的测试中使用它:
class RequireExampleTest {
@Test
fun testRequireFunction() {
val value = ""
require(value.isNotEmpty()) { "Value should not be empty" }
}
}
这个示例的输出使用 Power-assert 插件, 为失败的测试提供详细的信息:
Value should not be empty
require(value.isNotEmpty()) { "Value should not be empty" }
| |
| false
这段消息显示导致失败的中间值, 使得调试更加容易.
软断言(Soft Assertion)
Power-assert 插件支持软断言(Soft Assertion), 软断言不会让测试立即失败, 而是收集失败的断言, 并在测试运行结束时报告错误. 如果你想要通过一次运行看到所有失败的断言, 而不要在第一个失败的地方停止运行, 那么这个功能会很有用.
要启用软断言, 请实现收集错误消息的方法:
fun <R> assertSoftly(block: AssertScope.() -> R): R {
val scope = AssertScopeImpl()
val result = scope.block()
if (scope.errors.isNotEmpty()) {
throw AssertionError(scope.errors.joinToString("\n"))
}
return result
}
interface AssertScope {
fun assert(assertion: Boolean, message: (() -> String)? = null)
}
class AssertScopeImpl : AssertScope {
val errors = mutableListOf<String>()
override fun assert(assertion: Boolean, message: (() -> String)?) {
if (!assertion) {
errors.add(message?.invoke() ?: "Assertion failed")
}
}
}
添加这些函数到 powerAssert {}
代码块, 让 Power-assert 插件能够使用它们:
@OptIn(ExperimentalKotlinGradlePluginApi::class)
powerAssert {
functions = listOf("kotlin.assert", "kotlin.test.assert", "org.example.AssertScope.assert")
}
然后, 你可以在你的测试代码中使用它:
// 导入 assertSoftly() 函数
import org.example.assertSoftly
class SoftAssertExampleTest1 {
data class Employee(val name: String, val age: Int, val salary: Int)
@Test
fun `test employees data`() {
val employees = listOf(
Employee("Alice", 30, 60000),
Employee("Bob", 45, 80000),
Employee("Charlie", 55, 40000),
Employee("Dave", 150, 70000)
)
assertSoftly {
for (employee in employees) {
assert(employee.age < 100) { "${employee.name} has an invalid age: ${employee.age}" }
assert(employee.salary > 50000) { "${employee.name} has an invalid salary: ${employee.salary}" }
}
}
}
}
在输出中, 所有的 assert()
函数错误消息将会逐个打印输出:
Charlie has an invalid salary: 40000
assert(employee.salary > 50000) { "${employee.name} has an invalid salary: ${employee.salary}" }
| | |
| | false
| 40000
Employee(name=Charlie, age=55, salary=40000)
Dave has an invalid age: 150
assert(employee.age < 100) { "${employee.name} has an invalid age: ${employee.age}" }
| | |
| | false
| 150
Employee(name=Dave, age=150, salary=70000)