Se você está começando no mundo do desenvolvimento de software, talvez já tenha ouvido falar sobre testes automatizados. Eles ajudam a garantir que o seu código funciona como esperado. Um tipo específico de teste automatizado é o teste de aceitação, que verifica se o sistema atende aos requisitos do usuário final.
Neste artigo, vamos explorar como usar o Cucumber com a linguagem Kotlin para criar testes de aceitação de forma simples e eficaz.
Behavior-Driven Development (BDD) e Cucumber
Antes de mergulharmos no Cucumber, é importante entender o conceito de BDD (Behavior-Driven Development). BDD é uma metodologia de desenvolvimento que incentiva a colaboração entre desenvolvedores, QAs e stakeholders não-técnicos. Diferente do TDD (Test-Driven Development) que foca em testar código, o BDD foca em testar o comportamento do sistema a partir da perspectiva do usuário.
O Cucumber foi criado como uma ferramenta para suportar BDD, permitindo que todos os envolvidos no projeto possam contribuir para a definição dos testes, mesmo aqueles sem conhecimento técnico profundo.
O que é o Cucumber?
O Cucumber é uma ferramenta de automação de testes que permite escrever casos de teste em linguagem natural. Isso significa que você pode descrever como o sistema deve funcionar usando frases simples, fáceis de entender por qualquer pessoa, mesmo quem não é programador.
Ele usa a sintaxe Gherkin, que tem três palavras-chave principais:
- Dado (Given): Para configurar o estado inicial (o contexto do teste).
- Quando (When): Para descrever a ação que está sendo testada.
- Então (Then): Para definir o resultado esperado.
Funcionalidade: Calculadora
Cenário: Somar dois números
Dado que eu tenho uma calculadora
Quando eu somo 2 + 3
Então o resultado deve ser 5
Por que usar Cucumber com Kotlin?
O Kotlin é uma linguagem moderna, concisa e 100% compatível com o Java. Usar Cucumber com Kotlin permite escrever testes mais legíveis e menos verbosos. Além disso, o Kotlin tem uma sintaxe simples, o que facilita a criação e a manutenção de testes automatizados.
Alguns benefícios de usar Kotlin com Cucumber incluem:
- Código mais conciso e expressivo
- Segurança de tipo em tempo de compilação
- Interoperabilidade com bibliotecas Java existentes
- Recursos modernos de linguagem como funções de extensão e lambdas
Configurando o Ambiente
Para começar, você precisa configurar um projeto em Kotlin com o Cucumber. Vamos usar o Gradle para isso.
1. Criando um novo projeto Kotlin
mkdir cucumber-kotlin
cd cucumber-kotlin
gradle init --type kotlin-application
2. Atualizando as dependências
dependencies {
testImplementation("io.cucumber:cucumber-java:7.14.0")
testImplementation("io.cucumber:cucumber-junit:7.14.0")
testImplementation("org.jetbrains.kotlin:kotlin-test")
// Para relatórios
testImplementation("net.masterthought:cucumber-reporting:5.7.5")
// Para testes de UI (opcional)
testImplementation("org.seleniumhq.selenium:selenium-java:4.11.0")
}
tasks.test {
useJUnitPlatform()
}
3. Estrutura de Diretórios Recomendada
É importante organizar bem seu projeto para facilitar a manutenção. Uma estrutura recomendada seria:
src
├── main
│ └── kotlin
│ └── com
│ └── example
│ └── app
│ └── Calculator.kt
├── test
│ ├── kotlin
│ │ └── com
│ │ └── example
│ │ └── steps
│ │ ├── CalculatorSteps.kt
│ │ └── Hooks.kt
│ └── resources
│ ├── cucumber.properties
│ └── features
│ └── calculator.feature
Em seguida, execute:
gradle build
Escrevendo o Primeiro Teste
Vamos criar um teste para somar dois números.
1. Criando o arquivo de cenários
Dentro da pasta src/test/resources/features
, crie um arquivo chamado calculadora.feature
:
Funcionalidade: Calculadora
Cenário: Somar dois números
Dado que eu tenho uma calculadora
Quando eu somo 2 + 3
Então o resultado deve ser 5
2. Implementando os Passos em Kotlin
import io.cucumber.java.pt.*
import kotlin.test.assertEquals
class CalculadoraSteps {
private var resultado = 0
@Dado("que eu tenho uma calculadora")
fun queEuTenhoUmaCalculadora() {
println("Calculadora pronta para uso")
}
@Quando("eu somo {int} + {int}")
fun euSomarNumeros(a: Int, b: Int) {
resultado = a + b
}
@Então("o resultado deve ser {int}")
fun oResultadoDeveSer(esperado: Int) {
assertEquals(esperado, resultado)
}
}
3. Executando os Testes
gradle test
Se tudo estiver correto, você verá uma mensagem informando que o teste passou com sucesso!
Cenários mais Complexos: Login de Usuário
Vamos ver um exemplo mais elaborado de como usar Cucumber para testar um fluxo de login.
Primeiro, crie um arquivo login.feature
:
# language: pt
Funcionalidade: Login no Sistema
Contexto:
Dado que estou na página de login
@web @smoke
Cenário: Login bem-sucedido com credenciais válidas
Quando eu informo o usuário "usuario@exemplo.com"
E informo a senha "senha123"
E clico no botão Entrar
Então devo ser redirecionado para a página principal
E devo ver uma mensagem de boas-vindas com meu nome
@web
Cenário: Login malsucedido com credenciais inválidas
Quando eu informo o usuário "usuario@exemplo.com"
E informo a senha "senhaerrada"
E clico no botão Entrar
Então devo ver uma mensagem de erro "Credenciais inválidas"
E devo permanecer na página de login
@web
Esquema do Cenário: Tentativas de login com diferentes credenciais
Quando eu informo o usuário ""
E informo a senha ""
E clico no botão Entrar
Então devo ver uma mensagem ""
Exemplos:
| usuario | senha | mensagem |
| usuario@exemplo.com | senha123 | Bem-vindo, Usuário |
| usuario@exemplo.com | errada | Credenciais inválidas |
| naoexiste@exemplo.com| qualquer | Usuário não encontrado |
| | senha123 | Campo de usuário obrigatório |
E sua implementação em Kotlin (usando Selenium para testes de UI):
import io.cucumber.java.pt.*
import org.openqa.selenium.By
import org.openqa.selenium.WebDriver
import org.openqa.selenium.chrome.ChromeDriver
import kotlin.test.assertEquals
import kotlin.test.assertTrue
class LoginSteps {
private lateinit var driver: WebDriver
@Dado("que estou na página de login")
fun queEstouNaPaginaDeLogin() {
driver = ChromeDriver()
driver.get("http://exemplo.com/login")
}
@Quando("eu informo o usuário {string}")
fun euInformoOUsuario(usuario: String) {
if (usuario.isNotEmpty()) {
driver.findElement(By.id("usuario")).sendKeys(usuario)
}
}
@E("informo a senha {string}")
fun informoASenha(senha: String) {
driver.findElement(By.id("senha")).sendKeys(senha)
}
@E("clico no botão Entrar")
fun clicoNoBotaoEntrar() {
driver.findElement(By.id("btnEntrar")).click()
}
@Então("devo ser redirecionado para a página principal")
fun devoSerRedirecionadoParaAPaginaPrincipal() {
assertTrue(driver.currentUrl.contains("/dashboard"))
}
@E("devo ver uma mensagem de boas-vindas com meu nome")
fun devoVerUmaMensagemDeBoasVindas() {
val mensagem = driver.findElement(By.id("mensagem-boas-vindas")).text
assertTrue(mensagem.contains("Bem-vindo"))
}
@Então("devo ver uma mensagem de erro {string}")
fun devoVerUmaMensagemDeErro(mensagem: String) {
val textoErro = driver.findElement(By.id("mensagem-erro")).text
assertEquals(mensagem, textoErro)
}
@E("devo permanecer na página de login")
fun devoPermanecerNaPaginaDeLogin() {
assertTrue(driver.currentUrl.contains("/login"))
}
@Então("devo ver uma mensagem {string}")
fun devoVerUmaMensagem(mensagem: String) {
val textoMensagem = driver.findElement(By.cssSelector(".mensagem")).text
assertEquals(mensagem, textoMensagem)
}
}
Usando Tags e Hooks
As tags permitem categorizar seus cenários e executar seletivamente grupos específicos de testes. No exemplo acima, usamos as tags @web
e @smoke
.
Para executar apenas os testes marcados com uma tag específica:
gradle test -Dcucumber.filter.tags="@smoke"
Os hooks são blocos de código que são executados antes ou depois de cada cenário. Eles são úteis para configuração e limpeza:
import io.cucumber.java.After
import io.cucumber.java.Before
import io.cucumber.java.Scenario
class Hooks {
@Before
fun setUp(scenario: Scenario) {
println("Iniciando o cenário: ${scenario.name}")
// Configurar ambiente de teste
}
@Before("@web")
fun setUpWebTests() {
// Configuração específica para testes web
System.setProperty("webdriver.chrome.driver", "/path/to/chromedriver")
}
@After
fun tearDown(scenario: Scenario) {
if (scenario.isFailed) {
// Capturar screenshots ou logs em caso de falha
}
// Limpar recursos
println("Cenário finalizado: ${scenario.name} - Status: ${scenario.status}")
}
}
Configurando Relatórios
Relatórios visuais facilitam a análise dos resultados dos testes. Você pode configurar relatórios bonitos com o cucumber-reporting
:
import net.masterthought.cucumber.Configuration
import net.masterthought.cucumber.ReportBuilder
import java.io.File
import org.gradle.api.tasks.testing.Test
// Adicione ao seu build.gradle.kts
tasks.register<Test>("cucumberTests") {
dependsOn("testClasses")
doLast {
val reportOutputDir = File("build/reports/cucumber")
val reportConfig = Configuration(reportOutputDir, "Meu Projeto")
reportConfig.addClassifications("Plataforma", System.getProperty("os.name"))
reportConfig.addClassifications("Versão", "1.0.0")
val jsonFiles = File("build/cucumber-report").listFiles()?.filter { it.name.endsWith(".json") }
if (jsonFiles != null) {
val reportBuilder = ReportBuilder(jsonFiles.map { it.absolutePath }, reportConfig)
reportBuilder.generateReports()
}
}
}
Problemas Comuns e Soluções
Ao trabalhar com Cucumber e Kotlin, você pode encontrar alguns desafios:
- Passos não encontrados: Verifique se a anotação corresponde exatamente ao texto do cenário.
- Expressões regulares complexas: Use o cucumber-expressions em vez de regex para maior clareza.
- Acentuação em português: Certifique-se de que os arquivos estejam codificados em UTF-8.
- Compartilhamento de estado: Use injeção de dependência ou o padrão World para compartilhar estado entre passos.
Exemplo de solução para compartilhamento de estado:
// Classe para compartilhar estado entre steps
class TestContext {
var usuario: String = ""
var resultado: Any? = null
// Outros dados compartilhados
}
// Nos arquivos de steps
class LoginSteps(private val context: TestContext) {
@Quando("eu faço login como {string}")
fun euFacoLoginComo(usuario: String) {
context.usuario = usuario
// Resto da implementação
}
}
class DashboardSteps(private val context: TestContext) {
@Então("eu devo ver meu nome {string}")
fun euDevoVerMeuNome(nome: String) {
assertEquals(nome, getDisplayedUsername())
assertEquals(context.usuario, getUserFromSession())
}
}
Benefícios em Equipes Ágeis
O uso do Cucumber traz diversos benefícios para equipes que trabalham com metodologias ágeis:
- Comunicação melhorada: Todos os membros da equipe podem entender os cenários de teste.
- Documentação viva: Os cenários servem como documentação atualizada do sistema.
- Foco no valor de negócio: Os testes são escritos a partir da perspectiva do usuário.
- Feedback rápido: Problemas são identificados cedo no ciclo de desenvolvimento.
- Qualidade desde o início: Incentiva a pensar em casos de teste antes da implementação.
Dicas para Escrever Bons Testes
- Seja claro e objetivo: Os cenários devem ser fáceis de entender.
- Teste casos reais: Foque em situações que os usuários realmente irão enfrentar.
- Mantenha organizado: Separe os testes por funcionalidades.
- Evite detalhes técnicos: Mantenha seus cenários focados no comportamento, não na implementação.
- Reuse passos: Crie passos genéricos que possam ser reutilizados em diferentes cenários.
- Mantenha os cenários independentes: Cada cenário deve poder ser executado isoladamente.
Conclusão
Usar Cucumber com Kotlin é uma excelente forma de automatizar testes de aceitação. Você escreve os requisitos em linguagem simples e implementa os passos no código, garantindo que o sistema funcione como esperado.
Experimente em seu próximo projeto e veja como isso pode facilitar a manutenção e a qualidade do seu software!
Recursos Adicionais
- Documentação oficial do Cucumber
- Documentação do Kotlin
- Referência da sintaxe Gherkin
- Repositório do Cucumber-JVM no GitHub
- Tutorial: Integrando Cucumber com Kotlin
Inscreva-se também no canal no Youtube