Testes Simples com Cucumber + Kotlin

Testes Simples com Cucumber + Kotlin

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:

  1. Passos não encontrados: Verifique se a anotação corresponde exatamente ao texto do cenário.
  2. Expressões regulares complexas: Use o cucumber-expressions em vez de regex para maior clareza.
  3. Acentuação em português: Certifique-se de que os arquivos estejam codificados em UTF-8.
  4. 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

Inscreva-se também no canal no Youtube

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Rolar para cima