Power-assert 컴파일러 플러그인
Kotlin Power-assert 컴파일러 플러그인은 문맥 정보가 포함된 상세한 실패 메시지를 제공하여 디버깅 경험을 개선합니다. 이 플러그인은 실패 메시지에 중간값(intermediate values)을 자동으로 생성하여 테스트 작성 과정을 간소화합니다. 복잡한 어서션(assertion) 라이브러리 없이도 테스트가 실패한 이유를 쉽게 이해할 수 있도록 도와줍니다.
다음은 플러그인에서 제공하는 메시지 예시입니다:
Incorrect length
assert(hello.length == world.substring(1, 4).length) { "Incorrect length" }
| | | | | |
| | | | | 3
| | | | orl
| | | world!
| | false
| 5
HelloPower-assert 플러그인의 주요 기능:
- 개선된 에러 메시지: 플러그인은 어서션 내의 변수와 하위 표현식(sub-expressions)의 값을 캡처하고 표시하여 실패 원인을 명확하게 식별합니다.
- 간소화된 테스트: 유익한 실패 메시지를 자동으로 생성하여 복잡한 어서션 라이브러리의 필요성을 줄여줍니다.
- 여러 함수 지원: 기본적으로
assert()함수 호출을 변환하지만,require(),check(),assertTrue()와 같은 다른 함수들도 변환할 수 있습니다.
플러그인 적용하기
Gradle
Power-assert 플러그인을 활성화하려면 build.gradle(.kts) 파일을 다음과 같이 설정하세요:
// build.gradle.kts
plugins {
kotlin("multiplatform") version "2.3.0"
kotlin("plugin.power-assert") version "2.3.0"
}// build.gradle
plugins {
id 'org.jetbrains.kotlin.multiplatform' version '2.3.0'
id 'org.jetbrains.kotlin.plugin.power-assert' version '2.3.0'
}Power-assert 플러그인은 동작을 커스텀할 수 있는 몇 가지 옵션을 제공합니다:
functions: 정규화된 함수 경로(fully-qualified function paths)의 목록입니다. Power-assert 플러그인은 이 함수들에 대한 호출을 변환합니다. 지정하지 않으면 기본적으로kotlin.assert()호출만 변환됩니다.includedSourceSets: Power-assert 플러그인이 변환할 Gradle 소스 세트(source sets)의 목록입니다. 지정하지 않으면 기본적으로 모든 테스트 소스 세트(test source sets)가 변환됩니다.
동작을 커스텀하려면 빌드 스크립트 파일에 powerAssert {} 블록을 추가하세요:
// build.gradle.kts
powerAssert {
functions = listOf("kotlin.assert", "kotlin.test.assertTrue", "kotlin.test.assertEquals", "kotlin.test.assertNull")
includedSourceSets = listOf("commonMain", "jvmMain", "jsMain", "nativeMain")
}// build.gradle
powerAssert {
functions = ["kotlin.assert", "kotlin.test.assertTrue", "kotlin.test.assertEquals", "kotlin.test.assertNull"]
includedSourceSets = ["commonMain", "jvmMain", "jsMain", "nativeMain"]
}이 플러그인은 실험적(Experimental) 단계이므로, 앱을 빌드할 때마다 경고가 표시됩니다. 이 경고를 제외하려면 powerAssert {} 블록을 선언하기 전에 이 @OptIn 어노테이션을 추가하세요:
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
@OptIn(ExperimentalKotlinGradlePluginApi::class)
powerAssert {
...
}Maven
Maven 프로젝트에서 Power-assert 컴파일러 플러그인을 활성화하려면, pom.xml 파일의 kotlin-maven-plugin 설정 중 <plugin> 섹션을 업데이트하세요:
<build>
<plugins>
<plugin>
<artifactId>kotlin-maven-plugin</artifactId>
<groupId>org.jetbrains.kotlin</groupId>
<version>2.3.0</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>
<!-- Power-assert 플러그인 지정 -->
<compilerPlugins>
<plugin>power-assert</plugin>
</compilerPlugins>
</configuration>
<!-- Power-assert 플러그인 의존성 추가 -->
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-power-assert</artifactId>
<version>2.3.0</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>function 옵션을 사용하여 Power-assert 플러그인이 변환할 함수를 커스텀할 수 있습니다. 예를 들어 kotlin.test.assertTrue(), kotlin.test.assertEquals() 등을 포함할 수 있습니다. 지정하지 않으면 기본적으로 kotlin.assert() 호출만 변환됩니다.
kotlin-maven-plugin의 <configuration> 섹션에 이 옵션을 지정하세요:
<configuration>
<!-- 변환할 함수 지정 -->
<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의 전체 코드를 확인하세요:
// build.gradle.kts
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
plugins {
kotlin("jvm") version "2.3.0"
kotlin("plugin.power-assert") version "2.3.0"
}
group = "com.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", "com.example.AssertScope.assert")
}// build.gradle
plugins {
id 'org.jetbrains.kotlin.jvm' version '2.3.0'
id 'org.jetbrains.kotlin.plugin.power-assert' version '2.3.0'
}
group = 'com.example'
version = '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
testImplementation 'org.jetbrains.kotlin:kotlin-test'
}
test {
useJUnitPlatform()
}
powerAssert {
functions = [
'kotlin.assert',
'kotlin.test.assertEquals',
'kotlin.test.assertTrue',
'kotlin.test.assertNull',
'kotlin.require',
'com.example.AssertScope.assert'
]
}<!-- pom.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>maven-power-assert-plugin-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<kotlin.code.style>official</kotlin.code.style>
<kotlin.compiler.jvmTarget>1.8</kotlin.compiler.jvmTarget>
</properties>
<repositories>
<repository>
<id>mavenCentral</id>
<url>https://repo1.maven.org/maven2/</url>
</repository>
</repositories>
<build>
<sourceDirectory>src/main/kotlin</sourceDirectory>
<testSourceDirectory>src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>2.3.0</version>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
<configuration>
<compilerPlugins>
<plugin>power-assert</plugin>
</compilerPlugins>
<pluginOptions>
<option>power-assert:function=kotlin.assert</option>
<option>power-assert:function=kotlin.require</option>
<option>power-assert:function=kotlin.test.assertTrue</option>
<option>power-assert:function=kotlin.test.assertEquals</option>
<option>power-assert:function=kotlin.test.assertNull</option>
<option>power-assert:function=com.example.AssertScope.assert</option>
</pluginOptions>
</configuration>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-power-assert</artifactId>
<version>2.3.0</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.22.2</version>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<configuration>
<mainClass>MainKt</mainClass>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test-junit5</artifactId>
<version>2.3.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>2.3.0</version>
</dependency>
</dependencies>
</project>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더 완전한 오류 메시지를 얻으려면 항상 변수를 테스트 함수 파라미터에 인라인(inline)하세요. 다음 테스트 함수를 살펴보세요:
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 값을 가질 수 있는 형태라면 이들도 변환할 수 있습니다.
테스트에서 새 함수를 사용하기 전에 빌드 파일에 해당 함수를 추가하세요. 예를 들어 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 플러그인은 소프트 어서션(soft assertions)을 지원합니다. 소프트 어서션은 테스트를 즉시 실패 처리하지 않고, 대신 어서션 실패를 수집하여 테스트 실행이 끝날 때 보고합니다. 이는 첫 번째 실패에서 멈추지 않고 한 번의 실행으로 모든 어서션 실패를 확인하고 싶을 때 유용합니다.
소프트 어서션을 활성화하려면 에러 메시지를 수집하는 방식을 구현하세요:
fun <R> assertSoftly(block: AssertScope.() -> R): R {
val scope = AssertScopeImpl()
val result = scope.block()
if (scope.errors.isNotEmpty()) {
throw AssertionError(scope.errors.joinToString("
"))
}
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>
AssertScope.assert()함수를 선언한 패키지의 전체 이름을 지정해야 합니다.
그 후 테스트 코드에서 이를 사용할 수 있습니다:
// assertSoftly() 함수 임포트
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}" }
| | |
| | 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)다음 단계
- 플러그인이 활성화된 간단한 프로젝트 및 여러 소스 세트가 포함된 더 복잡한 프로젝트를 살펴보세요.
