Плагин компилятора Power-assert
Плагин компилятора Kotlin Power-assert упрощает отладку, предоставляя подробные сообщения об ошибках с контекстной информацией. Он упрощает написание тестов, автоматически добавляя промежуточные значения в сообщения об ошибках. С его помощью можно понять, почему тест завершился с ошибкой, без использования сложных библиотек для проверок.
Пример сообщения, предоставляемого плагином:
Incorrect length
assert(hello.length == world.substring(1, 4).length) { "Incorrect length" }
| | | | | |
| 5 | | "orl" 3
"Hello" | "world!"
false
Основные возможности плагина Power-assert:
Расширенные сообщения об ошибках: плагин фиксирует и отображает значения переменных и подвыражений внутри проверки, чтобы точно определить причину сбоя.
Библиотека времени выполнения: библиотека предоставляет аннотацию
@PowerAssertи классCallExplanation. Они упрощают обнаружение и настройку функций, поддерживающих Power-assert, благодаря их непосредственной интеграции с преобразованиями плагина компилятора.Упрощённое тестирование: плагин автоматически генерирует информативные сообщения об ошибках, уменьшая потребность в сложных библиотеках для проверок.
Поддержка нескольких функций: по умолчанию плагин преобразует вызовы функции
assert(), но может преобразовывать и другие функции, такие какrequire(),check()иassertTrue().
Подключение плагина
Gradle
Чтобы включить плагин Power-assert, настройте файл build.gradle(.kts) следующим образом:
// build.gradle.kts
plugins {
kotlin("multiplatform") version "2.4.20"
kotlin("plugin.power-assert") version "2.4.20"
}
// build.gradle
plugins {
id 'org.jetbrains.kotlin.multiplatform' version '2.4.20'
id 'org.jetbrains.kotlin.plugin.power-assert' version '2.4.20'
}
Плагин Power-assert предоставляет несколько параметров для настройки своего поведения:
functionsсодержит полные пути к функциям, вызовы которых преобразует плагин Power-assert. Если параметр не задан, плагин преобразует только вызовыkotlin.assert().-
compilationFilterопределяет, к каким компиляциям Kotlin применяется плагин Power-assert. Можно создать собственный фильтр или использовать предопределённые параметры:PowerAssertCompilationFilter.TESTSприменяется ко всем наборам исходного кода для тестов (по умолчанию).PowerAssertCompilationFilter.ALLприменяется ко всем наборам исходного кода.
Чтобы настроить поведение плагина, добавьте блок powerAssert {} в файл сценария сборки:
// build.gradle.kts
powerAssert {
functions = listOf("kotlin.assert", "kotlin.test.assertTrue", "kotlin.test.assertEquals", "kotlin.test.assertNull")
compilationFilter = PowerAssertCompilationFilter {
it.name in setOf("commonMain", "jvmMain", "jsMain", "nativeMain")
}
}
// build.gradle
powerAssert {
functions = ["kotlin.assert", "kotlin.test.assertTrue", "kotlin.test.assertEquals", "kotlin.test.assertNull"]
compilationFilter = PowerAssertCompilationFilter {
it.name in ["commonMain", "jvmMain", "jsMain", "nativeMain"]
}
}
Поскольку плагин является экспериментальным, при каждой сборке приложения будут отображаться предупреждения. Чтобы скрыть их, добавьте эту аннотацию @OptIn перед объявлением блока powerAssert {}:
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
@OptIn(ExperimentalKotlinGradlePluginApi::class)
powerAssert {
...
}
Maven
Чтобы включить плагин компилятора Power-assert в проекте Maven, обновите раздел <plugin> файла kotlin-maven-plugin в файле pom.xml:
<build>
<plugins>
<plugin>
<artifactId>kotlin-maven-plugin</artifactId>
<groupId>org.jetbrains.kotlin</groupId>
<version>2.4.20</version>
<executions>
<execution>
<id>compile</id>
<phase>process-sources</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<phase>process-test-sources</phase>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
<configuration>
<!-- Specify the Power-assert plugin -->
<compilerPlugins>
<plugin>power-assert</plugin>
</compilerPlugins>
</configuration>
<!-- Add the Power-assert plugin dependency -->
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-power-assert</artifactId>
<version>2.4.20</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
Настроить функции, преобразуемые плагином Power-assert, можно с помощью параметра function. Например, можно добавить kotlin.test.assertTrue(), kotlin.test.assertEquals() и другие функции. Если параметр не задан, по умолчанию преобразуются только вызовы kotlin.assert().
Укажите этот параметр в разделе <configuration> файла kotlin-maven-plugin:
<configuration>
<!-- Specify the functions to transform -->
<pluginOptions>
<option>power-assert:function=kotlin.assert</option>
<option>power-assert:function=kotlin.test.assertTrue</option>
<option>power-assert:function=kotlin.test.AssertEquals</option>
</pluginOptions>
</configuration>
Использование плагина Power-assert
В этом разделе приведены примеры использования плагина компилятора Power-assert.
Полный код файла сценария сборки build.gradle.kts или pom.xml со всеми этими примерами:
Функции с аннотацией @PowerAssert
Если функция помечена аннотацией @PowerAssert, плагин Power-assert автоматически преобразует вызовы этой функции. Регистрировать её в конфигурации сборки не требуется.
Можно добавлять аннотации @PowerAssert при объявлении собственных функций для проверок или использовать библиотеки с поддержкой Power-assert, предоставляющие функции с такими аннотациями.
Чтобы получать подробные сообщения об ошибках, вызывайте функцию в проекте с включённым плагином Power-assert:
import kotlin.test.Test
data class Mascot(val name: String)
class SampleTest {
@Test
fun testAnnotatedFunction() {
val subject: Any? = Mascot(name = "Unknown")
// If assertThat() is annotated with @PowerAssert in the library,
// the plugin transforms this call automatically
assertThat(subject) {
require(subject is Mascot)
check(subject.name == "Kodee")
}
}
}
Плагин предоставляет подробные сообщения об ошибках с промежуточными значениями выражений:
check(subject.name == "Kodee")
| | |
| | false
| "Unknown"
Mascot(name=Unknown)
Функция 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" }
}
}
Если запустить тест testFunction() с включённым плагином Power-assert, появится подробное сообщение об ошибке:
Incorrect length
assert(hello.length == world.substring(1, 4).length) { "Incorrect length" }
| | | | | |
| 5 | | "orl" 3
"Hello" | "world!"
false
Чтобы получить более полное сообщение об ошибке, всегда передавайте переменную непосредственно в качестве параметра тестовой функции. Рассмотрим следующую тестовую функцию:
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)
}
}
Вывод выполненного кода не содержит достаточно информации, чтобы найти причину проблемы:
assert(isValidName && isValidAge)
| |
true false
Передайте переменную непосредственно в функцию 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)
}
}
После выполнения вы получите более подробную информацию о том, что пошло не так:
assert(person.name.startsWith("A") && person.name.length > 3 && person.age > 20 && person.age < 29)
| | | | | | | | | |
| | true | | 5 true | 10 false
| "Alice" | "Alice" Person(name=Alice, age=10)
Person(name=Alice, age=10) Person(name=Alice, age=10)
Не только функция assert
Плагин Power-assert может преобразовывать различные функции, помимо assert, которая преобразуется по умолчанию. Можно также преобразовывать такие функции, как require(), check(), assertTrue(), assertEqual() и другие, если их сигнатура позволяет передать значение типа String или () -> String в качестве последнего параметра.
Перед использованием новой функции в тесте добавьте её в файл сборки. Например, функцию require():
// build.gradle.kts
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
@OptIn(ExperimentalKotlinGradlePluginApi::class)
powerAssert {
functions = listOf("kotlin.assert", "kotlin.require")
}
powerAssert {
functions = [
'kotlin.assert',
'kotlin.require'
]
}
<!-- pom.xml -->
<configuration>
<pluginOptions>
<option>power-assert:function=kotlin.assert</option>
<option>power-assert:function=kotlin.require</option>
</pluginOptions>
</configuration>
Добавив функцию, можно использовать её в тестах:
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
В сообщении показаны промежуточные значения, приведшие к ошибке, что упрощает отладку.
Мягкие проверки
Плагин Power-assert поддерживает мягкие проверки: они не прерывают тест сразу, а собирают ошибки проверок и сообщают о них в конце выполнения теста. Это полезно, когда нужно увидеть все ошибки проверок за один запуск, не останавливаясь на первой.
Чтобы включить мягкие проверки, реализуйте способ сбора сообщений об ошибках:
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")
}
}
}
Добавьте эти функции в файл сборки, чтобы сделать их доступными для плагина Power-assert:
// build.gradle.kts
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
@OptIn(ExperimentalKotlinGradlePluginApi::class)
powerAssert {
functions = listOf("kotlin.assert", "kotlin.test.assert", "com.example.AssertScope.assert")
}
powerAssert {
functions = [
'kotlin.assert',
'kotlin.test.assert',
'com.example.AssertScope.assert'
]
}
<!-- pom.xml -->
<configuration>
<pluginOptions>
<option>power-assert:function=kotlin.assert</option>
<option>power-assert:function=kotlin.require</option>
<option>power-assert:function=com.example.AssertScope.assert</option>
</pluginOptions>
</configuration>
После этого её можно использовать в тестовом коде:
// Import the assertSoftly() function
import com.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}" }
| | |
| 40000 false
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}" }
| | |
| 150 false
Employee(name=Dave, age=150, salary=70000)
Добавление поддержки Power-assert в библиотеку
Если вы автор библиотеки, можно добавить в неё встроенную поддержку Power-assert с помощью аннотации @PowerAssert и класса CallExplanation из библиотеки среды выполнения Power-assert.
Аннотация @PowerAssert
Аннотация @PowerAssert помечает функцию как поддерживающую Power-assert. Если в проектах пользователей вашей библиотеки подключён плагин компилятора Power-assert и они вызывают ваши функции с этой аннотацией, вызовы преобразуются автоматически, без дополнительной настройки сборки.
Чтобы добавить поддержку Power-assert в библиотеку:
В файле сборки подключите плагин Power-assert.
-
Для Maven добавьте библиотеку среды выполнения Power-assert в качестве зависимости:
<!-- pom.xml --> <dependencies> <dependency> <groupId>org.jetbrains.kotlin</groupId> <artifactId>kotlin-power-assert-runtime</artifactId> <version>2.4.20</version> </dependency> </dependencies>В Gradle эта зависимость добавляется автоматически вместе с плагином компилятора Power-assert.
-
Добавьте аннотацию
@PowerAssertк функциям для проверок:import kotlin.powerassert.PowerAssert import kotlin.powerassert.toDefaultMessage import kotlin.contracts.ExperimentalContracts import kotlin.contracts.contract @OptIn(ExperimentalContracts::class) @PowerAssert fun powerAssert(condition: Boolean, @PowerAssert.Ignore message: String? = null) { contract { returns() implies condition } if (!condition) { val explanation = PowerAssert.explanation ?: fail(message) val equalityErrors = buildList { for (expression in explanation.expressions) { if (expression is EqualityExpression && expression.value == false) { add(expression) } } } val failureMessage = buildString { if (message?.isNotBlank() == true) appendLine(message) append(explanation.toDefaultMessage()) } fail(failureMessage, equalityErrors) } }Свойство
PowerAssert.explanationпредоставляет доступ к объектуCallExplanation, содержащему информацию о месте вызова.Функция
toDefaultMessage()формирует стандартное сообщение об ошибке Power-assert.Аннотация
@PowerAssert.Ignoreдля параметраmessageисключает его из сообщения об ошибке.
Плагин компилятора обнаруживает аннотацию @PowerAssert и преобразует вызовы функции во время компиляции.
Класс CallExplanation
Класс CallExplanation предоставляет подробную информацию о месте вызова, в том числе о промежуточных значениях выражений. Это позволяет динамически формировать сообщения об ошибках проверок и улучшает интеграцию с внешними инструментами.
Если функция вашей библиотеки помечена аннотацией @PowerAssert и подключён плагин компилятора, преобразование автоматически выполняется в каждом месте вызова. Свойство PowerAssert.explanation предоставляет доступ к объекту CallExplanation внутри тела функции.
Ниже показано, как использовать CallExplanation внутри функций с аннотацией @PowerAssert, чтобы извлечь информацию об исходном коде и сформировать собственные сообщения об ошибках:
package kotlinx.test.fluent
import kotlin.powerassert.PowerAssert
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
@PowerAssert
fun AssertScope<*>.check(condition: Boolean) {
if (!condition) {
val explanation = PowerAssert.explanation
val message = if (explanation == null) null else {
val conditionArg = explanation.arguments.last()!!
val source = explanation.source.substring(conditionArg.startOffset, conditionArg.endOffset)
"Condition failed: $source"
}
collect(message, explanation)
}
}
@OptIn(ExperimentalContracts::class)
@PowerAssert
fun AssertScope<*>.require(condition: Boolean) {
contract { returns() implies condition }
if (!condition) {
val explanation = PowerAssert.explanation
val message = if (explanation == null) null else {
val conditionArg = explanation.arguments.last()!!
val source = explanation.source.substring(conditionArg.startOffset, conditionArg.endOffset)
"Condition failed: $source"
}
fail(message, explanation)
}
}
В этом примере функция check() собирает ошибки для последующего вывода, а функция require() немедленно завершает выполнение с ошибкой. Обе функции используют CallExplanation, чтобы извлечь исходный код условия, не прошедшего проверку, и включить его в сообщение об ошибке.
Что дальше
Ознакомьтесь с нашими примерами проектов:
© 2010–2026 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/power-assert.html