Tutorial: mockkStatic e slot em testes unitários com Kotlin + Spring Boot
Quando começamos a escrever testes unitários em Kotlin utilizando MockK, dois recursos aparecem com bastante frequência:
slot→ captura argumentos enviados para um método.mockkStatic→ faz mock de métodos estáticos (ou funções de alto nível do Kotlin).
1. O que é um Slot?
Imagine que você quer testar não apenas se um método foi chamado, mas qual objeto foi enviado para ele.
fun create(purchase: PurchaseModel) {
purchaseRepository.save(purchase)
applicationEventPublisher.publishEvent(
PurchaseEvent(this, purchase)
)
}O teste precisa garantir que:
- salvou a compra;
- publicou um evento;
- o evento contém a compra correta.
É aí que entra o slot.
Criando um Slot:
private var purchaseEventSlot = slot<PurchaseEvent>()Capturando o argumento
Ao verificar a chamada:
verify {
applicationEventPublisher.publishEvent(
capture(purchaseEventSlot)
)
}O capture() diz ao MockK:
“Toda vez que esse método for chamado, coloque o argumento dentro desse Slot.”
Depois disso podemos acessar. que retorna exatamente o objeto enviado.
KOTLINassertEquals( purchase, purchaseEventSlot.captured.purchaseModel )
Exemplo de uma classe completa com slot:
package mercado_livro.service
import io.mockk.*
import io.mockk.impl.annotations.InjectMockKs
import io.mockk.impl.annotations.MockK
import io.mockk.junit5.MockKExtension
import mercado_livro.events.PurchaseEvent
import mercado_livro.helper.buildPurchase
import mercado_livro.repository.PurchaseRepository
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.context.ApplicationEventPublisher
import kotlin.test.assertEquals
@ExtendWith(MockKExtension::class)
class PurchaseServiceTest {
@MockK
private lateinit var purchaseRepository: PurchaseRepository
@MockK
private lateinit var applicationEventPublisher: ApplicationEventPublisher
@InjectMockKs
private lateinit var purchaseService: PurchaseService
private var purchaseEvntSlot = slot<PurchaseEvent>()
@Test
fun `should create purchase and publish event`(){
val purchase = buildPurchase()
every { purchaseRepository.save(purchase) } returns purchase
every { applicationEventPublisher.publishEvent(any()) } just runs
purchaseService.create(purchase)
verify(exactly = 1) { purchaseRepository.save(purchase) }
verify(exactly = 1) { applicationEventPublisher.publishEvent(capture(purchaseEvntSlot)) }
assertEquals(purchase,purchaseEvntSlot.captured.purchaseModel)
}
}O que é mockkStatic?
Em dados que mudam toda vez como em UUID. O problema é que toda execução gera um valor diferente. Isso torna o teste imprevisível.
val nfe = UUID.randomUUID().toString()Como o UUID muda toda vez, não conseguimos fazer:
assertEquals(expected, generated)Solução
Transformamos o método estático em um mock.
mockkStatic(UUID::class)Agora o MockK intercepta qualquer chamada para:
UUID.randomUUID()Vamos ver uma classe de MockKStatic completa de testes:
package mercado_livro.events.listener
import io.mockk.*
import io.mockk.impl.annotations.InjectMockKs
import io.mockk.impl.annotations.MockK
import io.mockk.junit5.MockKExtension
import mercado_livro.events.PurchaseEvent
import mercado_livro.helper.buildPurchase
import mercado_livro.service.PurchaseService
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import java.util.*
@ExtendWith(MockKExtension::class)
class GenerateNfeListenerTest{
@MockK
private lateinit var purchaseService: PurchaseService
@InjectMockKs
private lateinit var generateNfeListener: GenerateNfeListener
@Test
fun `should generate nfe`(){
val purchase= buildPurchase(nfe = null)
val fakeNfe = UUID.randomUUID()
val purchaseExpected= purchase.copy(nfe = fakeNfe.toString())
mockkStatic(UUID::class)
every { UUID.randomUUID() } returns fakeNfe
every { purchaseService.update(purchaseExpected) } just runs
generateNfeListener.listener(PurchaseEvent(this,purchase))
verify(exactly = 1) { purchaseService.update(purchaseExpected) }
}
}
Para ver testes de integração, clique aqui.
0 comentários