Tag Archives: tdd

Para melhorar meu design, preciso mesmo fazer o teste antes?

Já comentei bastante sobre os efeitos que a prática de TDD tem no projeto de classes. Meu livro, aliás, é totalmente focado nisso. No entanto, algo que não comentei nele, porquê provavelmente só soube transformar isso em palavras há pouco tempo, é que não me importo muito se o teste é escrito antes ou depois. Ué, como assim!?

Deixe-me explicar. Não é a “prática de TDD” que faz seu projeto de classes melhorar, mas sim, o que você consegue ver de problemas ao olhar para o código do seu teste. Ou seja, é o teste que te mostra o problema. Se ele é escrito antes ou depois, tanto faz.

Obviamente, se você deixar para escrever o teste 1 semana depois de escrever a classe, na hora em que ler o feedback do teste, talvez seja tarde demais pra refatorar — tarde, no sentido, de caro, afinal você provavelmente precisará mudar em muitos pontos diferentes.

No entanto, se você gastar 15 minutos no código, 15 minutos no teste, 15 minutos no código, 15 minutos no teste…. o feedback será o mesmo! Afinal, para pensar no design, a implementação depende do teste e o teste depende da implementação; a ordem em que você faz não ter dará mais ou menos retorno.

O teste é só um rascunho do seu projeto de classes. E o importante não é ter um rascunho, ou mesmo começar por ele, mas sim rascunhar com frequência.

 

Mockar ou não mockar?

Com toda essa discussão do “Is TDD Dead?”, criada pelo DHH, as pessoas começaram a se perguntar se devem ou não fazer uso de mocks. Apesar do DHH ter sido infeliz no post polêmico, a discussão sobre mocks que ele levantou é bem importante. Tanto é que eu e o Hugo Corbucci fizemos uma palestra dedicada ao uso de mock objects, ano passado, na Agile Brazil.

Nela, discutimos as vantagens e desvantagens de usar ou não usar mocks, quando usar, quando não usar, e etc. Apesar da gravação não ter sido das melhores, é possível aprender bastante com ela.

Espero que gostem!

Mockar ou não mockar? – Hugo Corbucci e Mauricio Aniche from Agile Brazil on Vimeo.

Bate papo sobre TDD na Caelum

Há alguns meses atrás, organizei na Caelum um encontro sobre TDD. Nela tivemos a participação do Anderson Parra, falando sobre Fixture Factory (que divulgarei em breve também!), e depois um bate papo sobre pontos gerais de testes e TDD.

Abaixo segue o vídeo com a discussão na íntegra:

Se você gostou da discussão, não deixe de ver meu livro ou os cursos online do Alura sobre o assunto.

A quantidade de asserts em um teste indica algo? (CSMR2013)

Em março, apresentei um pequeno pedaço da minha pesquisa de doutorado no CSMR2013, em Gênova. O CSMR é uma conferência voltada para trabalhos que discutem manutenção e evolução de software.

Meu trabalho discute a relação entre a quantidade de asserts e a qualidade do código de produção. Será que o desenvolvedor faz uso de mais de 1 assert por teste porque o código de produção é complexo? É mais ou menos essa a pergunta que o trabalho visa responder.

No fim, acabei encontrando que a quantidade de asserts unicamente não indica nada: os desenvolvedores usam um número arbitrário de asserts mesmo com o código sendo simples. Mas algo interessante emergiu: se ao invés de contar a quantidade de asserts, eu contar a quantidade de diferentes objetos que recebem uma asserção e fizer a mesma comparação, isso sim indica que o código de produção pode ser mais complicado do que a média.

Em código, pra ficar mais fácil de entender:

assertEquals(“esperado”, a.getAlgumaCoisa());
assertEquals(“esperado2″, a.getAlgumaOutraCoisa());

Veja que estou fazendo 2 asserts, mas sempre no mesmo objeto “a”. Agora:

assertEquals(“esperado”, a.getAlgumaCoisa());
assertEquals(“esperado2″, b.getAlgumaOutraCoisa());

Veja que tenho 2 asserts em 2 objetos diferentes: “a” e “b”. Nesse caso, os métodos “getAlgumaCoisa()” e “getAlgumaOutraCoisa()” podem estar mais complicados do que deveriam. Portanto, acabei propondo o mau cheiro de teste que é “Mais de Uma Instância de Objeto Recebendo Asserção”. Repare nesse padrão, pois ele pode indicar problemas no seu código de produção.

Você pode ler o paper completo aqui se preferir! :)

Agile Tour 2011 em Poços de Caldas

Esse mês tive o prazer de fazer a palestra de abertura do Agile Tour Poços de Caldas 2011.

A palestra foi bem introdutória. Discuti sobre testes automatizados e TDD. Minha ideia maior foi motivar os participantes a escreverem testes automatizados, a conhecerem TDD, e a perceberem que dá pra fazer isso de maneira divertida! 😉

Seguem meus slides!

Testando datas (e métodos estáticos)

Muitas pessoas me perguntam como escrever testes de unidade de classes que lidam com datas. E, geralmente o problema está em como testar classes que usam a data/hora atual.

Esse problema acontece pois grande parte das APIs que lidam com datas, tanto no mundo .Net quanto no mundo Java, fazem uso de métodos estáticos. Por exemplo:

DateTime.Now // C#
Calendar.getInstance() // Java

Generalizando o problema, a dificuldade nao é testar datas, mas sim qualquer classe que faz uso de métodos estáticos. Por exemplo, como escrever um teste para o método abaixo?

public int DiasEntreHjEAData(DateTime data) {
  return (DateTime.Now - data).TotalDays;
}

A propriedade Now sempre irá devolver a data corrente, dificultando assim a escrita do teste; como escrever um teste onde o cenário muda o tempo todo?

Não conseguir simular o comportamento do método Now, e esse eh um dos problemas de usar métodos estáticos: dificulta o teste das classes que os utilizam (além de não permitir o uso decente de polimorfismo, mas isso é uma outra discussão…)

Para resolver esse problema, precisamos deixar de usar métodos estáticos. Mas e como fazer com as APIs que já existem, e não podemos mudá-las, como é o caso da API de DateTime?

Isso não nos impede de criarmos uma abstração em cima disso! Veja o código abaixo:

public interface Relogio {
  DateTime Hoje();
}

public class RelogioDoSistema : Relogio {
  public DateTime Hoje() {
    return DateTime.Now;
  }
}

Veja que criamos a interface Relogio, que abstrai o problema de calcular a hora atual. Nosso método acima agora, em vez de invocar o método estático, faz uso da nova abstração:

public class Algoritmo {

  // recebido pelo construtor
  private Relogio relogio;

  public int DiasEntreHjEAData(DateTime data) {
    return (relogio.Hoje() - data).TotalDays;
  }
}

Pronto. Veja agora que testar essa classe é facil. Basta passarmos um mock e simular o comportamento esperado do Relogio.

Resumindo, métodos estáticos dificultam a escrita de testes de unidade. Para resolver isso podemos: evitar a escrita de métodos estáticos, ou criar abstrações que escondem esses métodos. Nao é feio criar abstrações como a Relogio; feio é não testar! :)

Eu preciso de 100% de cobertura de testes?

Mito: não ter 100% de cobertura é a mesma coisa que nada!

Muitas pessoas discutem a necessidade de ter 100% de cobertura em um código. Mas não vejo problemas em códigos que não tenham 100% de cobertura de testes de unidade.

Acredito que isso deve ser uma meta da equipe, buscar sempre a maior cobertura possível; mas essa é uma meta que você provavelmente não vai alcançar; alguns trechos de código simplesmente não valem a pena serem testados de maneira isolada!

Explico: Veja essa classe do Restfulie.NET, por exemplo, chamada AspNetMvcUrlGenerator: ela serve para gerar URLs para Actions em Controllers, utilizando as rotas pré-definidas. Repare que ela faz uso intenso das APIs do Asp.Net MVC, utilizando inclusive alguns métodos estáticos (que sabemos que é difícil de testar) como no HttpContext.

public class AspNetMvcUrlGenerator : IUrlGenerator
    {
        public string For(string controller, string action, IDictionary values)
        {
            var httpContextWrapper = new HttpContextWrapper(HttpContext.Current);
            var urlHelper = new UrlHelper(new RequestContext(httpContextWrapper, 
              RouteTable.Routes.GetRouteData(httpContextWrapper)));

            return FullApplicationPath(httpContextWrapper.Request) + 
              urlHelper.Action(action, controller, new RouteValueDictionary(values));
        }

        private string FullApplicationPath(HttpRequestBase request)
        {
            var url = request.Url.AbsoluteUri.Replace(request.Url.AbsolutePath, 
               string.Empty) + request.ApplicationPath;
            return url.EndsWith("/") ? url.Substring(0, url.Length - 1) : url;
        }
    }

Eu até poderia ter feito alguma mágica e escrito um teste de unidade para esse código. Mas para quê? Apenas para aumentar o número? Não faz sentido! Esse trecho de código precisa de um teste de integração, e não de um teste de unidade!

Um outro exemplo é o teste de propriedades (Properties do C#). Preciso realmente deles? A própria linguagem implementou isso pra mim. O mesmo acontece no caso dos getters/setters do Java, onde o programador geralmente usa o Eclipse para gerá-los.

Você precisa cobrir seu código de testes, mas você pode usar testes de diferentes níveis para isso (testes de unidade, de integração, de sistema, etc)! Escrever testes de unidade inúteis, apenas para chegar nos 100% de cobertura, é desperdício.

Um pequeno estudo sobre asserções em testes

Muitas pessoas já ouviram falar da regra “apenas uma asserção por teste” (only one assertion per test), famosa no post do Dave Astels. A regra, como o próprio nome diz, afirma que o programador nunca deve escrever mais de uma asserção pois a necessidade de mais de uma asserção em um teste poderia indicar que o método está fazendo coisas demais.

Há um mês atrás comecei a escrever um post sobre isso, mas meu exemplo era muito fraco. Corri então para olhar testes de alguns dos últimos projetos que participei, e pasmém: na maioria deles só havia uma asserção!

Ao observar os testes que continham mais de uma asserção, percebi que eles aconteciam nos seguintes casos:

  1. Quando o método retorna uma lista, array ou uma classe responsável por armazenar uma coleção de determinado objeto;
  2. Quando o método retorna um novo objeto;
  3. Quando o método é parte de uma DSL;

Nos testes do tipo (1), as asserções mais comuns são para verificar o tamanho da lista e se o elemento existente nela é o esperado. Além disso, se o teste espera mais de um elemento na lista, o teste faz algum tipo de loop para verificar; Nos testes do tipo (2), o método de teste realiza asserções sobre os atributos do objeto retornado; E, finalmente, nos testes do tipo (3), que geralmente testam a DSL, e nesse caso um teste realmente testa mais de um comportamento ao mesmo tempo, e por isso, verifica mais de um comportamento.

Resolvi portanto perguntar a opinião de outros desenvolvedores sobre a regra. Para isso, enviei a pergunta no twitter (de forma não enviesada, perguntando apenas a opinião e, para os que me responderam de volta, exemplos de código aonde isso fizesse sentido).

Algumas pessoas concordaram com a ideia de apenas uma asserção por teste, afirmando que a regra ajuda a manter o código mais simples e mais fácil de manter. Além disso, quando o teste quebra, é fácil perceber o problema.

Outros já discordaram da regra, e afirmaram que o programador deve tentar escrever sempre o menor número possível de asserções, mas que a regra não precisa ser seguida à risca. Discutindo um pouco melhor esse ponto de vista, muitos deles afirmaram que um teste deve testar apenas uma única funcionalidade, não importando o número de asserções necessárias para tal.

Infelizmente a quantidade de códigos enviada foi muito baixa. Mas, o interessante é que um dos códigos enviados se encaixa em (1). Já o outro código enviado, retirado de um livro de Ruby, não se encaixa em nenhum dos exemplos acima, mas pode-se dizer que aquele teste está na verdade testando duas funcionalidades: string com espaços no começo e no fim e string sem espaços.

A ideia desse post é portanto, fomentar essa discussão. Alguém tem um outro exemplo aonde mais de uma asserção por teste faça sentido, mas que não se encaixa nos casos acima?

Agradecimentos

Obrigado ao @elemarjr, @viniciusquaiato, @FabioVazquez, @pedroreys, @pisketti, @danielsilvarj, @carlosgaldino, @rodbv, @cessor, @alexandregazola, que responderam no Twitter.

É “Test-Driven Design” e não “Design Done by Tests”

Muitos códigos legados possuem graves problemas de design. Classes gigantes que fazem tudo ou classes altamente acopladas são exemplos reais de código presentes no dia-a-dia de muitos desenvolvedores. E isso não é uma exceção: as leis da evolução do software mostram que o código de um software tende a degradar. O trabalho do programador é evitar que isso aconteça ou, no pior caso, diminuir a velocidade desse processo de apodrecimento do design.

A busca por um design perfeito, que esteja preparado para aceitar mudanças e evoluir de forma simples, é difícil. Por esse motivo, a prática de TDD tem sido muito comentada pois, segundo seus praticantes, ela ajuda o programador a criar um design melhor.

Mas, a mais famosa frase da área de engenharia de software já nos diz que não existe bala de prata. Nenhuma prática garante o sucesso de um projeto ou um código de qualidade. As práticas estão lá para tentar manter o programador nessa direção.

E é a mesma coisa com TDD: a prática não resolverá todos os problemas de design que um programador enfrentará. O programador, na verdade, utiliza os testes para guiar o design. É através dele que o programador sabe se está indo no caminho certo ou não. Isso não quer dizer que TDD faz o design sozinho para o programador. É óbvio que o programador precisa ter experiência e conhecimento necessários para que o design saia realmente com qualidade.

Mas um programador que tenha alto conhecimento e experiência em desenvolvimento também pode criar um design com a mesma qualidade. A diferença é que TDD (e os testes gerados) dão feedback muito mais rápido sobre a qualidade. O gráfico abaixo, feito pelo Gleen Vanderburg, mostra o tempo de feedback de várias práticas ágeis. Veja que TDD dá feedback em minutos, ou seja, em alguns minutos o programador tem informações sobre o seu design. Através dos testes ele pode obter informações como a coesão da classe, o acoplamento, a simplicidade, etc. Novamente, o programador usa sua experiência para receber e entender esse feedback.

Figura 1. Práticas e tempo de feedback

Essa é na verdade a grande diferença para o design feito pelo arquiteto-astronauta, famoso no modelo Waterfall, para os designs ágeis. O arquiteto pode ter muita experiência, mas o design que ele faz leva tanto tempo para ser validado e receber feedback que, quando isso acontece, o custo de mudança é altíssimo.

Além disso, o programador ao usar TDD (e por consequência guiar seu design através dos testes) é “forçado” a utilizar bons princípios de orientação a objetos. Os tão falados princípios SOLID passam a fazer mais sentido no momento em que o programador precisa escrever um código que seja fácil de testar. Parafraseando Feathers, existe uma grande sinergia entre código fácil de testar e código bom. Esses bons padrões facilitam o programador a escrever um código mais fácil de testar, apesar do Mark Seemann discordar.

Novamente, a experiência do programador conta. O programador experiente sabe que deve gerenciar as dependências entre classes (DIP), sabe que as classes devem ser coesas (SRP), sabe que elas devem evoluir sem a necessidade de reescrevê-la (OCP), e etc. Programadores que não usam TDD também podem fazer uso desses bons princípios. A diferença é que TDD dá feedback quase instantâneo: a necessidade da utilização dessas ideias aparece após alguns minutos programando, o que não é verdade quando o programador não faz TDD.

Enfim, TDD não faz milagre. Mas ele fica lembrando o programador constantemente sobre a necessidade de manter o código limpo. E a necessidade disso é evidente. Com certeza outros programadores irão encontrar outras práticas que também dão feedback sobre qualidade de design ao desenvolvedor. Mas, enquanto isso não acontece, eu recomendo a utilização de TDD.

Mas lembre-se: é design guiado pelos testes (Test-Driven Design) e não design feito pelos testes!