TDD e BDD em Aplicações Java EE com JUnit, Arquillian, Selenium e Cucumber, parte 1
Autor: Paulo Jerônimo | Publicado: 2013-04-08T01:04:00-03:00



Introdução


TDD e BDD na prática em apps Java EE, com JUnit, Arquillian, Selenium e Cucumber é o título do tutorial que submeti ao evento Agile Brazil 2013. Nesse tutorial, minha proposta é oferecer aos participantes os conceitos fundamentais, numa visão prática e através do uso de excelentes ferramentas, um guia para a construção de aplicações Java EE através de práticas que dirigem o desenvolvimento tornando-as testáveis e aceitáveis para o cliente, sob o ponto de vista de negócios. Meu desejo é que, ao término desse tutorial, qualquer desenvolvedor Java EE ainda não "infectado por uma metodologia baseada em testes" passe a sê-lo. =)


Este post apresenta o roteiro do tutorial que cobrirei no tempo esperado para sua realização (80 minutos), caso ele seja aprovado. Contudo, ele vai além pois também faz links com outros artigos e materiais disponíveis sobre o assunto na Internet (alguns de autoria própria), apresenta código que utilizo para explicar de conceitos que demonstrei há alguns anos em outros artigos e, por fim, faz uma breve introdução ao Cucumber. Até o dia do evento, escreverei partes complementares a esse material, também na forma de tutoriais e/ou vídeos, onde explicarei exemplos menos triviais e mais reais que demonstrarão, ainda, o uso integrado de ferramentas como o Arquillian, o Selenium e o Cucumber. Independente da submissão de minha palestra ser aprovada, isso deverá ocorrer pois esse material também tem importância fundamental para um trabalho que venho desempenhando num cliente, nesse instante.






Um pouco de história


TDD é uma prática antiga, aplicada na comunidade que atua com metodologias ágeis. Surgiu como uma técnica de eXtreme Programming (XP) e é um acrônimo para Test Driven Development. Particularmente, eu começei a me envolver com TDD um pouco antes da minha participação no evento eXtreme Programming Brasil 2002 quando Kent Beck<!-- (foto) -->, considerado o criador do XP e pai da ferramenta JUnit, esteve presente. Eu descrevo alguns dos benefícios e como iniciar a prática do TDD num artigo que escrevi e cujo título é "Test-Driven Development (TDD) em Java (Parte 1)". Nessa primeira parte, apresentei TDD com a preocupação de mostrar os benefícios da técnica e também de mostrar os fundamentos arquiteturais que deram origem ao framework JUnit.


Fiquei de escrever mais partes para esse artigo. Porém, nunca cheguei a fazer isso pois nos momentos em que eu precisava explicar mais detalhadamente o JUnit, eu fazia isso explicando diretamente o código Java que escrevi como continuação para o artigo e utilizava os slides sobre JUnit do amigo e parceiro Helder da Rocha, da Argo Navis. Seus slides foram apresentados no evento eXtreme Programming Brasil 2002 e incluenciaram muita gente, inclusive eu. =)


Então, passaram-se onze (11) anos desde que estive presente nesse evento e que conheci pessoas que fizeram, e ainda fazem, grande diferença neste mundo do desenvolvimento ágil de software. Surgiram novas metodologias e práticas aprimorando as existentes. Dentre elas o BDD, sigla para Behaviour Driven Development. Nunca cheguei a escrever sobre BDD e esta é a primeira vez que faço isso. Mas, desde a época em que o JBehave apareceu, eu já acreditava que BDD teria grande valor. O JBehave é um framework/ferramenta Java que implementa os conceitos de BDD e, tais conceitos, têm uma boa introdução no artigo "Introducing BDD" escrito por Dan North (pai do termo). Em português, há uma tradução desse artigo escrita pelo Rodrigo Urubatan. Também, quando ainda não existia suporte à nossa língua pelo JBehave, o Emerson Macedo criou uma extensão desse projeto que possiblitava a utilização de termos de BDD em português. Eu gosto de contar essas histórias pois elas demonstram o quanto os desenvolvedores brasileiros se preocupam com técnicas e ferramentas que auxiliem no desenvolvimento ágil.


O RSpec e o Cucumber são outras ferramentas para a aplicação de BDD. Surgiram na comunidade Ruby e foram amplamente adotados por ela. Mas, agora, o Cucumber pode ser executado em várias linguagens diferentes do Ruby pois poussui uma implementação para a JVM, denominada cucumber-jvm. Desenvolvido por Aslak Hellesoy, o mesmo cara que criou o XDoclet que, por sua sua vez, influenciou a introdução do conceito de anotações no Java. Aslak mantém o código do cucumber-jvm, com alguns outros committers, é muito ativo na lista de discussão da ferramenta e é um dos autores do livro "The Cucumber Book: Behaviour-Driven Development for Testers and Developers".



Mão na massa!


Cucumber Hello World


Partindo para a prática, agora eu apresentarei os conceitos básicos de BDD, através de uma aplicação do tipo "Hello World" que utiliza o cucumber-jvm. Sei que uma aplicação deste nível é básica demais para explorar totalmente o assunto. Contudo, ela é suficiente, num nível introdutório como o apresentado nesta parte, para que possamos entender e nos embasar melhor para explorar o assunto com um exemplo mais real num próximo post. Para me acompanhar nesta prática, eu espero apenas que você já tenha:



Acesso a um shell Bash: os comandos que apresento deverão copiados e colados diretamente neste shell (em Cygwin, Linux, Mac OS X, ...);

Um ambiente Java montado: JDK + Maven são suficientes (nos ambientes testados utilizei o JDK 1.7 e o Maven 3);




No papel de usuário (não desenvolvedor) escreveremos uma estória que guiará os desenvolvedores na implementação do comportamento esperado para nossa aplicação. No papel de desenvolvedor, você copiará e colará as linhas a seguir no seu shell:



d=/tmp/cucumber-jvm-helloworld; rm -rf $d &amp;&amp; mkdir -p $d &amp;&amp; cd $d
d=src/test/resources/com/ladoservidor/cucumber/helloworld; mkdir -p $d
cat &gt; $d/helloworld.feature &lt;&lt;'EOF'
Feature: Hello World

  Scenario: Say hello
    Given I have a hello app with "Hello"
    When I ask it to say hi
    Then it should answer with "Hello World"
EOF



Então, os comandos apresentados nas linhas acima criarão o diretório da aplicação (/tmp/cucumber-jvm-helloworld) e, dentro dele, criarão a estória definida pelo usuário (no arquivo src/test/resources/com/ladoservidor/cucumber/helloworld/helloworld.feature).


A "estória do usuário" é definida como uma "Feature" (funcionalidade ou característica). Nela há um "Scenario" (cenário) denominado Say hello. Este cenário apresenta uma situação (Given), uma ação (When) e um estado esperado (Then). Observe que tudo isto é explícito numa linguagem natural (o inglês). Em resumo, é isto: as estórias de usuário devem ser definidas numa linguagem natural e simples (não técnicas). Nessas estórias são definidos cenários que, por sua vez, indicam que dada uma determinada situação (Given), quando algo ocorrer (When), então (Then) um determinado estado será esperado. Simples assim.


Apesar de natural, a linguagem utilizada para a definição da estória não é tão livre assim. Ela é uma DSL (domain specific language), que faz parte do Cucumber e que é chamada de Gherkin. As palavras Feauture, Scenario, Given, When, Then e algumas outras, são definidas nesta linguagem.


Como desenvolvedores, agora faremos uso do Cucumber para que possamos tornar testáveis os cenários definidos em uma funcionalidade do usuário. Sendo assim, nosso próximo passo será criar um POM para o projeto que será implementado e testável através do Cucumber. Então, copie o código abaixo e cole em seu shell:



cat &gt; pom.xml &lt;&lt;'EOF'
&lt;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"&gt;

    &lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;
    &lt;groupId&gt;com.ladoservidor&lt;/groupId&gt;
    &lt;artifactId&gt;cucumber-jvm-helloworld&lt;/artifactId&gt;
    &lt;version&gt;1.0&lt;/version&gt;
    &lt;packaging&gt;jar&lt;/packaging&gt;
    &lt;name&gt;cucumber-jvm/HelloWorld&lt;/name&gt;

    &lt;build&gt;
        &lt;plugins&gt;
            &lt;plugin&gt;
                &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
                &lt;artifactId&gt;maven-compiler-plugin&lt;/artifactId&gt;
                &lt;version&gt;2.5.1&lt;/version&gt;
                &lt;configuration&gt;
                    &lt;encoding&gt;UTF-8&lt;/encoding&gt;
                    &lt;source&gt;1.6&lt;/source&gt;
                    &lt;target&gt;1.6&lt;/target&gt;
                &lt;/configuration&gt;
            &lt;/plugin&gt;

            &lt;plugin&gt;
                &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
                &lt;artifactId&gt;maven-surefire-plugin&lt;/artifactId&gt;
                &lt;version&gt;2.12.2&lt;/version&gt;
                &lt;configuration&gt;
                    &lt;useFile&gt;false&lt;/useFile&gt;
                &lt;/configuration&gt;
            &lt;/plugin&gt;
        &lt;/plugins&gt;
    &lt;/build&gt;

    &lt;dependencies&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;info.cukes&lt;/groupId&gt;
            &lt;artifactId&gt;cucumber-java&lt;/artifactId&gt;
            &lt;version&gt;1.1.2&lt;/version&gt;
            &lt;scope&gt;test&lt;/scope&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;info.cukes&lt;/groupId&gt;
            &lt;artifactId&gt;cucumber-junit&lt;/artifactId&gt;
            &lt;version&gt;1.1.2&lt;/version&gt;
            &lt;scope&gt;test&lt;/scope&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;junit&lt;/groupId&gt;
            &lt;artifactId&gt;junit&lt;/artifactId&gt;
            &lt;version&gt;4.11&lt;/version&gt;
            &lt;scope&gt;test&lt;/scope&gt;
        &lt;/dependency&gt;
    &lt;/dependencies&gt;
&lt;/project&gt;
EOF



Utilizando o Maven na construção da aplicação, o Cucumber se beneficiará da estrutura do JUnit para a realização dos testes que criaremos a seguir. Também poderíamos utilizar o Ant para criar um build.xml e disparar um executor (Runner) do Cucumber através de uma linha de comando (ele possui, então, dois runners: o cucumber-core e o cucumber-junit). Um exemplo que apresenta o uso de um build.xml é disponibilizado na aplicação java-helloworld que vem como exemplo junto ao código fonte do Cucumber. Eu utilizo essa aplicação, com pequenas alterações, para escrever este post.


Até este ponto, já temos a seguinte estrutura montada em nosso sistema de arquivos:



.
├── pom.xml
└── src
    └── test
        └── resources
            └── com
                └── ladoservidor
                    └── cucumber
                        └── helloworld
                            └── helloworld.feature



Nossos próximos passos serão a criação de dois arquivos. O primeiro será responsável por configurar um runner para o Cucumber que utilizará o JUnit. O segundo será um TestCase JUnit, com anotações Cucumber, que intepretará uma funcionalidade (arquivo .feature) descrito pelo usuário.


Copie e cole o seguinte conteúdo, em teu shell, para criar o primeiro arquivo:



d=src/test/java/com/ladoservidor/cucumber/helloworld; mkdir -p $d
cat &gt; $d/RunCukesTest.java &lt;&lt;'EOF'
package com.ladoservidor.cucumber.helloworld;

import cucumber.api.junit.Cucumber;
import org.junit.runner.RunWith;

@RunWith(Cucumber.class)
@Cucumber.Options(
  format = {
    "pretty", 
    "html:target/cucumber-html-report", 
    "json-pretty:target/cucumber-json-report.json"
  }
)
public class RunCukesTest {
}
EOF



Isso criará a classe com.ladoservidor.cucumber.helloworld.RunCukesTest que, como podemos perceber, possui a anotação @RunWith do JUnit recebendo Cucumber.class. Desta forma, na fase de testes do Maven (ao invocarmos, por exemplo, o comando "mvn test"), quando ele localizar classes de teste para serem testadas com o JUnit, ele repassará o controle da execução dos testes para o Cucumber.


A anotação @Cucumber.Options pode receber parâmetros opcionais como, por exemplo, format, features e outras listadas na documentação da anotação. O parâmetro format configura opções de formatação para a saída dos testes executados pelo Cucumber e que poderiam, também, ser passadas via linha de comando para o runner cucumber-core. Em nosso caso, estamos informado ao Cucumber (com o valor pretty) que ele deverá imprimir uma saída para cada passo de um cenário e apresentá-la com uma cor que represente o resultado da sua execução (passou, falhou, estava indefinido ou ignorado). Além disso, a saída deverá gerar um relatório em HTML (para a execução de cada cenário) no diretório target/cucumber-html-report e, este relatório, lerá o arquivo target/cucumber-json-report.json como input.


Neste ponto, execute o comando "mvn test". Após sua execução, e caso as bibliotecas já estejam no repositório local padrão (~/.m2/repository), uma saída semelhante a esta deverá ser gerada:



[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building cucumber-jvm/HelloWorld 1.0
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- maven-resources-plugin:2.4.3:resources (default-resources) @ cucumber-jvm-helloworld ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /private/tmp/cucumber-jvm-helloworld/src/main/resources
[INFO] 
[INFO] --- maven-compiler-plugin:2.5.1:compile (default-compile) @ cucumber-jvm-helloworld ---
[INFO] No sources to compile
[INFO] 
[INFO] --- maven-resources-plugin:2.4.3:testResources (default-testResources) @ cucumber-jvm-helloworld ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 1 resource
[INFO] 
[INFO] --- maven-compiler-plugin:2.5.1:testCompile (default-testCompile) @ cucumber-jvm-helloworld ---
[INFO] Compiling 1 source file to /private/tmp/cucumber-jvm-helloworld/target/test-classes
[INFO] 
[INFO] --- maven-surefire-plugin:2.12.2:test (default-test) @ cucumber-jvm-helloworld ---
[INFO] Surefire report directory: /private/tmp/cucumber-jvm-helloworld/target/surefire-reports

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running com.ladoservidor.cucumber.helloworld.RunCukesTest


You can implement missing steps with the snippets below:

@Given("^I have a hello app with \"([^\"]*)\"$")
public void I_have_a_hello_app_with(String arg1) throws Throwable {
    // Express the Regexp above with the code you wish you had
    throw new PendingException();
}

@When("^I ask it to say hi$")
public void I_ask_it_to_say_hi() throws Throwable {
    // Express the Regexp above with the code you wish you had
    throw new PendingException();
}

@Then("^it should answer with \"([^\"]*)\"$")
public void it_should_answer_with(String arg1) throws Throwable {
    // Express the Regexp above with the code you wish you had
    throw new PendingException();
}

Feature: Hello World

  Scenario: Say hello                        # com/ladoservidor/cucumber/helloworld/helloworld.feature:3
    Given I have a hello app with "Hello"
    When I ask it to say hi
    Then it should answer with "Hello World"
Tests run: 5, Failures: 0, Errors: 0, Skipped: 4, Time elapsed: 0.719 sec

Results :

Tests run: 5, Failures: 0, Errors: 0, Skipped: 4

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.270s
[INFO] Finished at: Sun Apr 07 20:58:33 BRT 2013
[INFO] Final Memory: 11M/143M
[INFO] ------------------------------------------------------------------------



Veja que o Cucumber interpretou o conteúdo do arquivo de funcionalidade existente (.feature) e sugeriu como deveríamos iniciar a implementação dos passos definidos no cenário descrito (ele sugere isso criando métodos anotados com @Given, @When e @Then). 


Observe, também, a estrutura do diretório target criada após a execução do "mvn test". Perceba que um relatório com a execução de um cenário foi gerado no diretório configurado (cucumber-html-report).



target
├── cucumber-html-report
│   ├── formatter.js
│   ├── index.html
│   ├── jquery-1.8.2.min.js
│   ├── report.js
│   └── style.css
├── cucumber-json-report.json
├── generated-test-sources
│   └── test-annotations
├── surefire-reports
│   └── TEST-com.ladoservidor.cucumber.helloworld.RunCukesTest.xml
└── test-classes
    └── com
        └── ladoservidor
            └── cucumber
                └── helloworld
                    ├── RunCukesTest.class
                    └── helloworld.feature

9 directories, 9 files



Nossa próxima tarefa será, então, criar um código com a definição dos passos que deveremos implementar para um dado cenário. Seguiremos a sugestão do Cucumber para o nome dos métodos e para o conteúdo das anotações. Mas, faremos nossa implementação inicial dos métodos conforme o código a seguir (mais uma vez, copie e cole os comandos abaixo em teu shell):



cat &gt; $d/HelloStepdefs.java &lt;&lt;'EOF'
package com.ladoservidor.cucumber.helloworld;

import cucumber.api.java.en.Given;
import cucumber.api.java.en.Then;
import cucumber.api.java.en.When;

import static org.junit.Assert.assertEquals;

public class HelloStepdefs {
    private Hello hello;
    private String hi;

    @Given("^I have a hello app with \"([^\"]*)\"$")
    public void I_have_a_hello_app_with(String greeting) {
        hello = new Hello(greeting);
    }

    @When("^I ask it to say hi$")
    public void I_ask_it_to_say_hi() {
        hi = hello.sayHi();
    }

    @Then("^it should answer with \"([^\"]*)\"$")
    public void it_should_answer_with(String expectedHi) {
        assertEquals(expectedHi, hi);
    }
}
EOF



A classe criada (com.ladoservidor.cucumber.helloworld.HelloStepdefs) faz a implementação dos passos definidos no cenário especificado (Say hello). Observe que, na implementação do método anotado com @Given, a tarefa é construir um objeto que representa a app Hello, definida na especificação do cenário. Então, é criada uma instância de uma classe que será construída a seguir (com.ladoservidor.cucumber.helloworld.Hello). O conteúdo da anotação é uma expressão regular (ER) que "casa" com o conteúdo definido para Given na funcionalidade especificada (arquivo .feature). Observe que, como sugerido pelo Cucumber, faremos o uso de um parâmetro especificado entre aspas na ER, cujo valor será passado para o parâmetro greeting do método I_have_a_hello_app_with. A instância que criaremos de Hello receberá o valor do parâmetro greeting.


No método anotado por @When definiremos, como expliquei, a ação sugerida na especificação da cenário. Nesse caso, a ação será invocar um método responsável por "dizer algo". Sendo assim, faremos uso da instância de Hello (criada na chamada ao método @Given) e invocaremos o método sayHi salvando sua resposta no objeto hi.


No método anotado por @Then temos a responsabilidade de averiguar uma mudança de estado ocorrida (@When) dada uma determinada situação (@Given). Pela descrito no cenário, dado que a app hello recebe 'Hello' como parâmetro, quando solicitarmos que ela diga algo, ela deverá responder 'Hello World'. E, é esta verificação que devemos implementar. Através da assertiva assertEquals do JUnit verificaremos se o valor recebido como conteúdo do parâmetro da ER repassado para expectedHi é o que salvamos no objeto hi.


Veja que, assim como na prática de TDD, com BDD definimos a classe que criaremos (Hello), seus métodos (sayHi) e seu comportamento esperado dada uma determinada situação. Sendo assim, partiremos agora para a criação da classe Hello: (copie e cole os comandos a seguir em teu shell)



d=src/main/java/com/ladoservidor/cucumber/helloworld
mkdir -p $d
cat &gt; $d/Hello.java &lt;&lt;'EOF'
package com.ladoservidor.cucumber.helloworld;

public class Hello {
    private final String greeting;

    public Hello(String greeting) {
        this.greeting = greeting;
    }

    public String sayHi() {
        return greeting + " World";
    }
}  
EOF



Pronto! Agora, podemos executar novamente o comando "mvn test" que deverá conter, na saída gerada, o seguinte trecho de texto: (execute esse comando e verifique sua saída para checar se estamos de acordo)



-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running com.ladoservidor.cucumber.helloworld.RunCukesTest

Feature: Hello World

  Scenario: Say hello                        # com/ladoservidor/cucumber/helloworld/helloworld.feature:3
    Given I have a hello app with "Hello"    # HelloStepdefs.I_have_a_hello_app_with(String)
    When I ask it to say hi                  # HelloStepdefs.I_ask_it_to_say_hi()
    Then it should answer with "Hello World" # HelloStepdefs.it_should_answer_with(String)
Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.945 sec

Results :

Tests run: 4, Failures: 0, Errors: 0, Skipped: 0



Utilizando o português


Para completar as tarefas práticas desta primeira parte vamos nacionalizar o uso do Cucumber alterando nossa aplicação "Hello World" para "Olá Mundo!".


Isto é muito válido já que, como citei, o interessante é que as estórias de usuários possam ser escritas, ou pelo menos, totalmente compreendidas e validadas, por eles. Sendo assim, pensando em usuários nacionais, nada mais correto que o amplo uso do português do Brasil para a definição dos arquivos de funcionalidades (arquivos ".feature").


Então, para migrar nossa aplicação, nosso primeiro passo será alterar o código da funcionalidade. Copie e cole o seguinte código em teu shell:



d=src/test/resources/com/ladoservidor/cucumber/helloworld
cat &gt; $d/helloworld.feature &lt;&lt;'EOF'
# language: pt
Funcionalidade: Diga Olá

  Cenário: Dizer "Olá Fulano!"
    Dado que eu tenho uma app que recebe "Paulo"
    Quando eu pedir que ela diga olá
    Então ela deveria responder "Olá Paulo!"
EOF



Observe que, para o suporte ao português, iniciamos o arquivo de funcionalidade com o comentário # language: pt. A seguir, utilizamos as palavras equivalentes ao que teríamos em inglês: 'Funcionalidade' para 'Feature', 'Cenário' para 'Scenario', e assim por diante. Note que agora há uma diferença sutil, em relação ao comportamento da aplicação, que eu especifiquei para esta versão em português do Brasil. Perceba esta mudança lendo a definição do cenário.


Para termos a apresentação dos relatórios em português, também precisaremos alterar o comportamento do Runner do Cucumber. O patch a seguir aplica esta mudança. (novamente, copie e cole o código abaixo em teu shell):



patch -p1 &lt;&lt;'EOF'
--- ./src/test/java/com/ladoservidor/cucumber/helloworld/RunCukesTest.java  2013-04-05 15:44:14.000000000 -0300
+++ ../HelloWorld.pt/src/test/java/com/ladoservidor/cucumber/helloworld/RunCukesTest.java 2013-04-05 15:45:15.000000000 -0300
@@ -8,7 +8,8 @@ import org.junit.runner.RunWith;
   format = {
     "pretty", 
     "html:target/cucumber-html-report", 
-    "json-pretty:target/cucumber-json-report.json"
+    "json-pretty:target/cucumber-json-report.json",
+    "json:target/cucumber-pt.json"
   }
 )
 public class RunCukesTest {
EOF



Como estamos agora descrevendo os passos dos cenários em português, faremos uso das anotações correspondentes a nossa língua. Então, alteraremos o código do arquivo HelloStepdefs.java pela aplicação do patch a seguir:



patch -p1 &lt;&lt;'EOF'
--- ./src/test/java/com/ladoservidor/cucumber/helloworld/HelloStepdefs.java 2013-04-05 15:44:14.000000000 -0300
+++ ../HelloWorld.pt/src/test/java/com/ladoservidor/cucumber/helloworld/HelloStepdefs.java  2013-04-05 15:45:15.000000000 -0300
@@ -1,8 +1,8 @@
 package com.ladoservidor.cucumber.helloworld;
 
-import cucumber.api.java.en.Given;
-import cucumber.api.java.en.Then;
-import cucumber.api.java.en.When;
+import cucumber.api.java.pt.Dado;
+import cucumber.api.java.pt.Quando;
+import cucumber.api.java.pt.Entao;
 
 import static org.junit.Assert.assertEquals;
 
@@ -10,17 +10,17 @@ public class HelloStepdefs {
     private Hello hello;
     private String hi;
 
-    @Given("^I have a hello app with \"([^\"]*)\"$")
+    @Dado("^que eu tenho uma app que recebe \"([^\"]*)\"$")
     public void I_have_a_hello_app_with(String greeting) {
         hello = new Hello(greeting);
     }
 
-    @When("^I ask it to say hi$")
+    @Quando("^eu pedir que ela diga olá$")
     public void I_ask_it_to_say_hi() {
         hi = hello.sayHi();
     }
 
-    @Then("^it should answer with \"([^\"]*)\"$")
+    @Entao("^ela deveria responder \"([^\"]*)!\"$")
     public void it_should_answer_with(String expectedHi) {
         assertEquals(expectedHi, hi);
     }
EOF



Por fim, você deve ter percebido uma pequena alteração no comportamento esperado pelo usuário e, isso, deve ser alterado em nossa implementação para o método sayHi, após a aplicação do patch a seguir:



patch -p1 &lt;&lt;'EOF'
--- ./src/main/java/com/ladoservidor/cucumber/helloworld/Hello.java 2013-04-05 15:44:14.000000000 -0300
+++ ../HelloWorld.pt/src/main/java/com/ladoservidor/cucumber/helloworld/Hello.java  2013-04-05 15:45:15.000000000 -0300
@@ -8,6 +8,6 @@ public class Hello {
     }
 
     public String sayHi() {
-        return greeting + " World";
+        return "Olá " + greeting;
     }
 }  
EOF



E, isso é tudo o necessário para a nacionalização da nossa aplicação Hello World. Testando a execução da funcinalidade descrita através da execução do Maven (comando "mvn test"), deveremos ter uma saída que conterá o seguinte texto:



-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running com.ladoservidor.cucumber.helloworld.RunCukesTest

# language: pt
Funcionalidade: Diga Olá

  Cenário: Dizer "Olá Fulano!"                   # com/ladoservidor/cucumber/helloworld/helloworld.feature:4
    Dado que eu tenho uma app que recebe "Paulo" # HelloStepdefs.I_have_a_hello_app_with(String)
    Quando eu pedir que ela diga olá             # HelloStepdefs.I_ask_it_to_say_hi()
    Então ela deveria responder "Olá Paulo!"     # HelloStepdefs.it_should_answer_with(String)
Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.933 sec

Results :

Tests run: 4, Failures: 0, Errors: 0, Skipped: 0



Próximos capítulos


As próximas partes deste tutorial cobrirão exemplos mais realistas, com mais cenários, e utilizando, também, testes com Arquillian para a camada de modelo da aplicação que será apresentada. Explicarei, de forma mais conceitual, os benefícios que a prática do BDD pode trazer. Em seguida, apresentarei testes na camada de apresentação utilizando o Selenium.


Ao concluir todas as partes deste tutorial, eu espero que você compreenda o uso integrado de todas as ferramentas que cito aqui (JUnit, Arquillian, Selenium e Cucumber) numa aplicação Java EE completa e real.