Se você já escreveu testes em Kotlin, sabe que uma das maiores dores de cabeça é decifrar mensagens de erro genéricas. Aquele AssertionError: expected true but was false que não diz absolutamente nada sobre o que deu errado. Para resolver isso, muita gente recorre a bibliotecas como AssertJ ou Kotest Assertions — mas e se o próprio compilador pudesse gerar mensagens detalhadas automaticamente?

É exatamente isso que o Power-Assert faz. Trata-se de um plugin oficial do compilador Kotlin, mantido pela JetBrains, que transforma assertions simples em mensagens ricas mostrando o valor de cada subexpressão. Neste artigo, vamos configurar o Power-Assert do zero, explorar exemplos práticos e entender quando ele substitui (ou complementa) bibliotecas de assertion tradicionais.

Se você já domina o básico de testes em Kotlin, confira nosso guia completo de testes com JUnit 5 e MockK para contexto adicional.

O que é o Power-Assert?

O Power-Assert é um plugin do compilador Kotlin (não uma biblioteca runtime) que intercepta chamadas a funções como assert(), require(), check() e assertTrue() durante a compilação. Quando uma assertion falha, em vez de exibir uma mensagem genérica, ele gera um diagrama mostrando o valor de cada variável e expressão intermediária.

O conceito nasceu com o Groovy Power Assert e foi adaptado para Kotlin por Brian Norman no projeto kotlin-power-assert. A partir do Kotlin 2.0, a JetBrains integrou o plugin oficialmente ao compilador K2, tornando-o um cidadão de primeira classe do ecossistema.

Diferente de bibliotecas como AssertJ que exigem uma API fluente (assertThat(valor).isEqualTo(esperado)), o Power-Assert funciona com o assert nativo do Kotlin. Você escreve código idiomático e o compilador faz o trabalho pesado.

Configurando o Power-Assert no Gradle

A configuração é feita no build.gradle.kts. Primeiro, adicione o plugin:

// build.gradle.kts
plugins {
    kotlin("jvm") version "2.3.20"
    kotlin("plugin.power-assert") version "2.3.20"
}

Em seguida, configure quais funções o plugin deve transformar:

import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi

@OptIn(ExperimentalKotlinGradlePluginApi::class)
powerAssert {
    functions = listOf(
        "kotlin.assert",
        "kotlin.require",
        "kotlin.check",
        "kotlin.test.assertTrue",
        "kotlin.test.assertEquals",
        "kotlin.test.assertNull"
    )
}

Não esqueça de adicionar a dependência de testes:

dependencies {
    testImplementation(kotlin("test"))
}

Se você usa Gradle com Kotlin DSL, esse setup já é familiar. O plugin funciona com Kotlin 2.0+ e é compatível com projetos Kotlin Multiplatform — basta configurar os includedSourceSets adequados.

Como funciona na prática

Vamos começar com um exemplo simples. Considere este teste:

import kotlin.test.Test

class UsuarioTest {

    @Test
    fun `nome do usuario deve ter pelo menos 3 caracteres`() {
        val nome = "Al"
        assert(nome.length >= 3) { "Nome muito curto" }
    }
}

Sem o Power-Assert, a falha exibe apenas:

Nome muito curto

Com o Power-Assert habilitado, a saída vira:

Nome muito curto
assert(nome.length >= 3) { "Nome muito curto" }
       |    |      |
       Al   2      false

Cada subexpressão é decomposta e alinhada verticalmente. Você vê imediatamente que nome vale "Al", que length é 2 e que a comparação >= 3 resultou em false. Sem precisar adicionar logs ou usar debugger.

Exemplo com expressões complexas

O Power-Assert brilha quando a assertion envolve múltiplas condições:

data class Funcionario(val nome: String, val idade: Int, val salario: Double)

@Test
fun `dados do funcionario devem ser validos`() {
    val func = Funcionario("Carlos", 150, 8500.0)

    assert(func.idade in 18..65 && func.salario > 5000.0 && func.nome.isNotBlank())
}

A saída do Power-Assert:

Assertion failed
assert(func.idade in 18..65 && func.salario > 5000.0 && func.nome.isNotBlank())
       |    |     |           |
       |    150   false       false
       Funcionario(nome=Carlos, idade=150, salario=8500.0)

Repare que o plugin identifica exatamente qual parte da expressão falhou (idade in 18..65 retornou false) e mostra o valor 150. Isso é infinitamente mais útil do que um AssertionError genérico.

Usando com assertTrue e assertEquals

O plugin não se limita ao assert. Quando você configura kotlin.test.assertTrue e kotlin.test.assertEquals na lista de funções, eles também ganham mensagens detalhadas:

import kotlin.test.Test
import kotlin.test.assertTrue
import kotlin.test.assertEquals

class CalculadoraTest {

    fun somar(a: Int, b: Int) = a + b

    @Test
    fun `soma deve retornar valor correto`() {
        val resultado = somar(2, 3)
        assertEquals(6, resultado)
    }

    @Test
    fun `resultado deve ser positivo e par`() {
        val valor = somar(3, 4)
        assertTrue(valor > 0 && valor % 2 == 0)
    }
}

No segundo teste, a saída mostra:

Assertion failed
assertTrue(valor > 0 && valor % 2 == 0)
           |     |      |     |   |
           7     true   7     1   false

Fica claro que 7 % 2 resulta em 1, que não é igual a 0. Se você usa JUnit 5 com MockK no dia a dia, o Power-Assert complementa bem o workflow sem exigir mudanças na sua API de testes.

Soft assertions com Power-Assert

Uma técnica avançada é combinar o Power-Assert com soft assertions — onde múltiplas falhas são coletadas e reportadas juntas no final do teste, em vez de parar na primeira:

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")
        }
    }
}

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
}

Para que o Power-Assert transforme a função assert dentro do AssertScope, adicione-a na configuração:

powerAssert {
    functions = listOf(
        "kotlin.assert",
        "com.exemplo.AssertScope.assert"  // sua função customizada
    )
}

Isso funciona porque o Power-Assert aceita qualquer função cujo último parâmetro seja String ou () -> String. Você pode criar suas próprias funções de assertion e o plugin gera os diagramas automaticamente.

Quando usar e quando não usar

O Power-Assert é excelente para:

  • Testes unitários com assertions simples usando assert, assertTrue, assertEquals
  • Validações em tempo de execução com require() e check() — veja nosso artigo sobre null safety para contexto
  • Projetos que valorizam simplicidade e não querem adicionar bibliotecas de assertion pesadas
  • Projetos multiplataforma via KMP onde você quer assertions consistentes em todas as plataformas

Porém, ele não substitui completamente bibliotecas como Kotest Assertions quando você precisa de:

  • Matchers especializados (coleções, strings, exceções)
  • Assertions assíncronas para coroutines e Flow
  • DSLs de assertion complexas com encadeamento fluente

A melhor abordagem é combinar: use Power-Assert para assertions diretas e uma biblioteca complementar para casos mais sofisticados. Se você está montando uma suíte de testes do zero, confira nosso tutorial de testes unitários para a base.

Limitações atuais

Algumas coisas para ficar atento:

  1. O plugin é experimental — a API pode mudar entre versões do Kotlin, embora esteja estável desde o Kotlin 2.0
  2. Requer K2 — funciona apenas com o compilador K2 (Kotlin 2.0+). Se seu projeto ainda usa o compilador legado, será preciso migrar primeiro
  3. Expressões inline geram melhor output — se você armazena o resultado de uma subexpressão em uma variável antes do assert, o diagrama perde contexto
  4. Não funciona com assertThat do AssertJ ou APIs fluentes — o Power-Assert precisa de uma expressão booleana direta

Power-Assert no contexto do ecossistema Kotlin

O Power-Assert se encaixa na filosofia do Kotlin de oferecer ferramentas poderosas integradas ao compilador. Assim como as sealed classes eliminaram a necessidade de bibliotecas de pattern matching, e as data classes substituíram geradores de boilerplate, o Power-Assert reduz a dependência de bibliotecas externas para testes.

Se você trabalha com Spring Boot e Kotlin, a integração é transparente — basta adicionar o plugin ao Gradle e seus testes existentes com assert ganham mensagens melhores sem nenhuma mudança de código.

Para quem está montando um projeto novo, confira também nosso post sobre Kotlin Scripting — outra feature subestimada do ecossistema que complementa bem a experiência de desenvolvimento. Se você trabalha com outras linguagens, vale comparar: Rust já oferece mensagens de assert detalhadas nativamente, e Go usa uma abordagem diferente com testify — cada ecossistema resolve testes de um jeito.

Perguntas frequentes

O Power-Assert funciona com Kotlin Multiplatform?

Sim. O plugin suporta KMP nativamente. Basta configurar os includedSourceSets para incluir os source sets desejados (commonTest, jvmTest, iosTest, etc.).

Preciso remover minhas bibliotecas de assertion existentes?

Nao. O Power-Assert complementa bibliotecas como Kotest e AssertJ. Você pode usar ambos no mesmo projeto sem conflitos.

O Power-Assert afeta a performance dos testes?

O impacto é insignificante. A transformação acontece em tempo de compilacao, nao em runtime. O unico overhead adicional e a construcao das mensagens de erro quando uma assertion falha — o que so ocorre em cenarios de falha.

Funciona com Gradle Version Catalog?

Sim. Basta declarar o plugin no version catalog e referencia-lo no build.gradle.kts usando o alias definido no catalogo.