Coding Dojo Floripa

Desenvolvimento Ágil

Archive for Outubro, 2006

Apresentando Behaviour Driven Development (BDD)

Posted by Ivan Sanchez em Sábado, Outubro 28, 2006

(Aviso! Esta não é mais uma nova técnica de programação)

Um dos maiores obstáculos na adoção do Desenvolvimento Orientado a Testes, ou Test Driven Development (TDD) é justamente o termo “teste”.

Talvez tenha sido uma bola fora do Kent Beck ter colocado este nome, já que a principal vantagem desta abordagem não é validação de código e sim seu uso para projetá-lo. Como diz o Ron Jeffries, “o objetivo do TDD é escrever um código limpo e que funcione”, ou seja, usamos TDD para nos ajudar escrever código, e não simplesmente testá-lo.

Felizmente este problema de vocabulário não impediu a popularizacão da técnica, porém frameworks como o jUnit continuaram batendo na tecla dos “testes” usando termos como TestCase, TestSuite e obrigando ao programador começar todos os métodos com “test”. Isso colaborou ainda mais na associação de TDD com validação, ao invés de especificação.

Quando escrevemos um “teste” antes de sequer termos a implementação, estamos especificando o comportamento esperado do nosso código, definindo um contexto de entrada e avaliando se as saídas são as esperadas. Esta especificação automatizada, embora de baixo nível, ajuda principalmente na implementação do código, além de servir como ferramenta de validação.

Para tentar desassociar TDD apenas ao conceito de testes, surgiu o termo Behaviour Driven Development (BDD), ou Desenvolvimento orientado a Comportamento. Também foram criados frameworks como o jBehave e RSpec para popularizar um novo vocabulário, mais condizente com os principais benefícios de TDD.

Alguns exemplos deste novo vocabulário incluem:

  • Context ao invés de TestCase
  • Specification ao invés de Test
  • Should ao invés de Assert

Quem já esteja acostumado com TDD e sabe exatamente o que esta técnica compreende talvez continue usando os seus termos, mas muitas confusões seriam evitadas se adotássemos este novo vocabulário.

Para quem quiser saber mais sobre BDD:

Posted in BDD, Geral, TDD | 7 Comments »

Kata no Rio de Janeiro: menos papo, mais ação

Posted by Ivan Sanchez em Quinta-feira, Outubro 26, 2006

“Show me the code!”. Essa foi a proposta da última reunião do grupo de eXtreme Programming do Rio de Janeiro: a XP Rio on Rails. Em quase duas horas de reunião o pessoal da ImproveIt apresentou a solução completa de um problema usando Ruby On Rails combinado com as técnicas de XP.

Este é um ótimo exemplo de kata, e pelo que vi na lista XPRio o evento foi um sucesso. Parabéns para o Vinicius Teles, Marcelo Alvim e Marcos Tapajós pela iniciativa 🙂

Obs.: recomento aos participantes do dojo participarem também da lista XPRio, que atualmente conta com mais de 500 participantes e é a mais ativa lista de XP no país.

Posted in Blogroll, Dojo | 4 Comments »

Variações de Dojo

Posted by Ivan Sanchez em Quinta-feira, Outubro 26, 2006

Existem atualmente duas formas de se conduzir uma reunião de Coding Dojo:

  • Randori: é a maneira descrita atualmente no sobre deste dojo. Todos os presentes se revezam no computador em duplas para resolver um desafio explicado anteriormente. A vantagem desta abordagem está na maior participação de todos e consequentemente maior troca de experiência. Porém infelizmente só é indicada para grupos pequenos, já que como a comunicação entre os participantes é maior, também fica mais fácil virar uma confusão. Esta foi a técnica utilizada na nossa Primeira reunião.
  • Kata: é a forma de se conduzir uma sessão de programação para um número maior de participantes. Os apresentadores demonstram a solução de um problema, o Kata, que já foi previamente ensaiada por eles, mostrando passo-a-passo como proceder. Neste caso não há revezamento com a platéia, que se limita a tirar dúvidas. Na parte final pode haver um tempo reservado apenas para discussão do que foi implementado, e essa é a oportunidade dos presentes trocarem suas experiências. A cada sessão podem surgir melhorias no kata, decorrentes das sessões anteriores.

Embora cada abordagem possua suas vantagens e desvantagens, ambas preservam a comunicação através do código e troca de experiências, que são a essência do dojo. Portanto, nossas próximas reuniões poderão seguir qualquer uma delas, só vai depender do número de participantes.

Posted in Dojo | 8 Comments »

Mocks vs Stubs: qual a diferença afinal?

Posted by Ivan Sanchez em Sexta-feira, Outubro 20, 2006

Mock Objects representam uma abordagem para simular objetos dentro dos testes. Isso nos ajuda quando o objeto real é muito complexo, lento ou ainda nem existe. Até este ponto mocks são bem parecidos com stubs. A diferença entre eles está nas abordagens que eles propõem:

  • Com stubs, nos preocupamos em testar o estado dos objetos após a execução do método. Neste caso incluimos os asserts para ver se o método nos levou ao estado que esperamos.
  • Com mocks, a preocupação é testar a interação entre objetos durante a execução do método. Neste caso, os asserts servem para ver se os métodos se relacionaram como o esperado.

Exemplo: usando Mocks para testar Servlets

Se usássemos mocks ao invés de stubs no nosso exemplo de stubs, teríamos algo como:

import static org.easymock.EasyMock.*;

public class ServletHelloWorldTest extends TestCase

{

    public void testService() throws ServletException, IOException

    {

        ServletHelloWorld servlet = new ServletHelloWorld();

        HttpServletRequest request = createMock(HttpServletRequest.class);

        HttpServletResponse response = createMock(HttpServletResponse.class);

        HttpSession session = createMock(HttpSession.class);

        expect(request.getParameter("nome")).andReturn(null);

        expect(request.getSession()).andReturn(session);

        replay(request);

        session.setAttribute("mensagem", "Hello World");

        replay(session);

        servlet.service(request, response);

        verify(request);

        verify(session);

    }

    public void testServiceComParametro() throws ServletException, IOException

    {

        ServletHelloWorld servlet = new ServletHelloWorld();

        HttpServletRequest request = createMock(HttpServletRequest.class);

        HttpServletResponse response = createMock(HttpServletResponse.class);

        HttpSession session = createMock(HttpSession.class);

        expect(request.getParameter("nome")).andReturn("João");

        expect(request.getSession()).andReturn(session);

        replay(request);

        session.setAttribute("mensagem", "Hello João");

        replay(session);

        servlet.service(request, response);

        verify(request);

        verify(session);

    }

}

Neste exemplo usamos o framework EasyMock que permite gerar os objetos mocks dentro dos testes, sem necessidade de implementar classes adicionais. Percebam que no teste, seguimos os seguintes passos:

  1. Criamos os mocks todos da mesma maneira, utilizando o comando createMock().
  2. Após a sua criação,utilizamos o método expect() para definir o comportamento que os métodos do mock terão ao serem invocados.
  3. Depois, usamos o replay() para indicar que os mocks estão prontos para uso e,
  4. Finalmente deixamos que o próprio framework verifique se a interação entre o objeto testado e o mock ocorreu como o esperado, através do método verify().

Desta forma conseguimos testar com a mesma eficiência dos stubs, mas com a vantagem de não precisarmos implementar nenhuma classe extra. Este também é o motivo que normalmente utilizamos stubs apenas quando estes são necessários, enquanto os mocks podem ser utilizados largamente nos testes quando queremos aumentar o isolamento entre as classes que estão sendo testadas.

Posted in TDD | 1 Comment »

Trabalhando com Stubs

Posted by Ivan Sanchez em Terça-feira, Outubro 17, 2006

Stubs são classes que simulam o comportamento de classes mais complexas através de uma implementação simples. Com eles é possível isolar a classe testada do resto do sistema, simplificando os testes e deixando-os mais independentes.

Exemplo: usando Stubs para testar Servlets

Para ver como criar stubs que facilitam os testes, veja este exemplo de um servlet de Hello World, utilizando recursos como passagem de parâmetros e sessão. Começamos escrevendo nosso primeiro teste:

public void testService() throws ServletException, IOException
{
	ServletHelloWorld servlet = new ServletHelloWorld();
	// criando stubs
	StubHttpServletRequest request = new StubHttpServletRequest();
	StubHttpServletResponse response = new StubHttpServletResponse();
	// chamando servlet
	servlet.service(request, response);
	// verificando resultados
	String mensagem = (String) request.getSession().getAttribute("mensagem");
	assertNotNull("Deveria haver uma mensagem na sessão", mensagem);
	assertEquals("Hello World", mensagem);
}

Nosso objetivo aqui é ignorar a existência de um container web (Tomcat, por exemplo) e mesmo assim queremos avaliar o comportamento do servlet. Para isso, criamos três classes implementando as respectivas interfaces da API de servlets:

public class StubHttpServletRequest implements HttpServletRequest
public class StubHttpServletResponse implements HttpServletResponse
public class StubHttpSession implements HttpSession

Uma vez criadas estas classes, podemos também criar o nosso servlet:

public class ServletHelloWorld extends HttpServlet
{
	protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
	{
	}
}

Todo código compila agora, e podemos começar a rodar os testes. O primeiro problema que surge é um NullPointerException ao tentar buscar a sessão do request, portanto, vamos evoluir as classe StubHttpServletRequest e StubHttpSession:

public class StubHttpServletRequest implements HttpServletRequest
{
	StubHttpSession session = new StubHttpSession();
	public HttpSession getSession()
	{
		return session;
	}
	....
}
public class StubHttpSession implements HttpSession
{
	Map attributes = new HashMap();
	public Object getAttribute(String arg0)
	{
		return attributes.get(arg0);
	}
	public void setAttribute(String arg0, Object arg1)
	{
		attributes.put(arg0, arg1);
	}
	...
}

Agora podemos rodar o teste novamente e verificar que um assert falha (“Deveria haver uma mensagem na sessão”). Isto significa que podemos finalmente implementar nosso servlet:

public class ServletHelloWorld extends HttpServlet
{
	protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
	{
		request.getSession().setAttribute("mensagem", "Hello World");
	}
}

A partir de então nosso teste passa. Podemos evoluir nosso servlet, usando uma passagem de um parâmetro a ser guardado junto com a mensagem. Para isso, criamos um novo teste:

public void testServiceComParametro() throws ServletException, IOException
{
	ServletHelloWorld servlet = new ServletHelloWorld();
	// criando stubs
	StubHttpServletRequest request = new StubHttpServletRequest();
	StubHttpServletResponse response = new StubHttpServletResponse();
	// definindo parâmetro a ser passado para o servlet
	request.setupGetParameter("nome", "João");
	// chamando servlet
	servlet.service(request, response);
	// verificando resultados
	String mensagem = (String) request.getSession().getAttribute("mensagem");
	assertNotNull("Deveria haver uma mensagem na sessão", mensagem);
	assertEquals("Hello João", mensagem);
}

Observe que agora precisamos criar um método setupGetParameter que não existe na interface HttpServletRequest. Porém, ele que permite que deixemos o stub no estado correto para que o servlet o acesse. Aqui está o novo trecho inserido na classe StubHttpServletRequest:

	Map parameters = new HashMap();
	public void setupGetParameter(String name, String value)
	{
		parameters.put(name, value);
	}
	public String getParameter(String arg0)
	{
		return (String)parameters.get(arg0);
	}

Mais uma vez temos um assert que falha, já que a lógica não foi definida no servlet. Então fazemos a alteração no método service:

protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
	String nome = "World";
	String parametro = request.getParameter("nome");
	if (parametro != null)
	{
		nome = parametro;
	}
	request.getSession().setAttribute("mensagem", "Hello " + nome);
}

Finalmente, todos os testes passam e ficamos satisfeitos. Conseguimos testar nosso servlet e começamos a criar uma implementação simples dos recursos do container web para ser reaproveitado nos nossos próximos testes.

Posted in TDD | 3 Comments »

Impressões sobre o 1° Dojo

Posted by Ivan Sanchez em Segunda-feira, Outubro 16, 2006

Começamos nossa reunião com uma breve apresentação do desafio e das regras do Dojo. Dos 7 participantes, a maioria já trabalhava com TDD usando jUnit e Eclipse, de tal modo que pudemos em poucos minutos criar o projeto e começar a resolver o desafio.

Um fato que chamou atenção foi a necessidade que alguns sentiram de fazer alguma modelagem antes de começar a programar os testes. Talvez possamos incluir algum tempo para trocar mais idéias sobre o desafio e até usar algo parecido com cartões CRC para definir ao menos a classe inicial da nossa implementação. De qualquer forma, uma vez criado o primeiro teste, as coisas começaram a fluir.

Durante a sessão a própria elaboração dos testes serviu como modelagem. A cada teste criado outros tantos já eram pensados. A cada dúvida íamos parando e discutindo o que estava acontecendo, e deste modo os participantes iam trocando experiências enquanto caminhávamos com a programação.

Aliás, este foi um dos pontos fortes do treino. Embora a maioria dos participantes se conhecesse, nem todos trabalhavam juntos (tivemos representantes de 3 empresas) e foi possível ver como cada ambiente ajuda a formar programadores com costumes diferentes.

Ao final de 3 horas conseguimos chegar perto do final do desafio, e o código fonte criado está disponível abaixo. Esperamos poder repetir a dose em breve 🙂

dojo_20061019_poker.zip

Posted in Dojo | Leave a Comment »

Fake it (til you make it)

Posted by Ivan Sanchez em Quarta-feira, Outubro 11, 2006

A técnica Fake it consiste em:

Escreva um teste que falhe. Para fazê-lo passar, utilize constantes. Vá escrevendo novos testes e gradualmente substituindo as constantes por variáveis.

Um exemplo simples: gostaria que um método me informasse se um número é par ou ímpar. Neste caso, eu posso começar com o seguinte teste:

public void testNumeroPar()
{
    assertTrue(verificador.ehPar(4));
}

Eu poderia tentar escrever a lógica deste teste, mas vou fazê-lo passar inicialmente com uma constante:

public boolean ehPar(int valor)
{
    return true;
}

O teste passa, mas é fácil ver que o método não faz tudo que queremos, o que me obriga a escrever um novo teste:

public void testNumeroImpar()
{
    assertFalse(verificador.ehPar(3));
}

Mais uma vez, posso usar uma implementação utilizando constantes. Então, modificamos o método ehPar:

public boolean ehPar(int valor)
{
    if(valor==3){
        return false;
    }
    return true;
}

Mais uma vez os testes passam, mas sinto que não cheguei onde eu queria. Vou escrever outro teste:

public void testNumeroImparSete()
{
    assertFalse(verificador.ehPar(7));
}

Agora meu teste falha, mas eu posso fazê-lo passar aproveitando para remover a duplicação da constante “3” (que está tanto no teste quanto no método ehPar):

public boolean ehPar(int valor)
{
    return (valor % 2 == 0) ? true : false;
}

Todos os testes passam e eu posso assumir que meu método está completo.

Esta técnica, embora seja simples, pode causar aversão à primeira vista. No exemplo acima é fácil imaginar a solução após escrever o primeiro testes, então o programador desavisado pode pensar: “por que diabos vou retornar uma constante?”. Simples: isto te obrigará a escrever novos testes e a pensar um pouco mais sobre o problema que está tentando se solucionar. Além disso, no final você terá vários testes para provar que a sua implementação funciona. Portanto, esta técnica trás dois benefícios:

  • Aumenta a confiança: uma vez que você terá vários testes para provar que a implementação continua funcionando mesmo depois de um refactoring.
  • Ajuda a controlar o escopo: já que você pode tratar um problema isolando um pedaço dele e gradativamente adicionando complexidade à solução.

Apesar de às vezes ir contra a natureza criativa dos programadores, esta técnica nos ajuda a trabalhar com um foco bem definido, o tempo todo.

Mais detalhes sobre esta técnicas podem ser encontrados no capítulo 28 (Testing Patterns) do livro Test-driven Development By Example, do Kent Beck.

Posted in TDD | 3 Comments »

1ª Reunião do Dojo

Posted by Ivan Sanchez em Terça-feira, Outubro 10, 2006

Data: 10 de Outubro de 2006, 19:00 hrs
Local: Binara Informática (R. Felipe Schmidt, 752 – Centro – Florianópolis-SC)

Descrição do desafio:

Criar um identificador de mãos de poker. Dada uma série de cartas (mão), o software deve indicar qual jogada ela representa (par,trinca, quadra, full house etc). Este desafio também foi utilizado no CodingDojo Finland e o ranking das cartas do jogo pode ser encontrada aqui.

Posted in Dojo | 7 Comments »