Test Data Builders: você está usando corretamente?

Test Data Builder é um padrão criado por Nat Pryce, um dos autores do excelente livro “Growing Object-Oriented Software, Guided By Tests”. O padrão, como o próprio nome sugere, toma emprestada a ideia de outro padrão original do GoF, “Builder”, para criar dados de entrada para nossos testes.

Seu uso “casa” bem com testes, porque permite criar dados (objetos) somente com as partes (atributos) que nos interessam para o teste em questão. Esta é uma característica fundamental dos builders – criar objetos “em partes” – que normalmente desenvolvedores se esquecem (ou desconhecem) quando os estão utilizando em código de teste.

Neste artigo, vamos ver dois erros frequentes em código de testes quanto ao uso de builders. (Se você quiser saber mais sobre o padrão Builder e Test Data Builders já escrevi sobre eles nestes artigos: [aqui] e [aqui].)

PROBLEMA 1: DADOS DE MENOS

Suponha que a SUT receba um paciente e deva retornar uma lista de brindes para ele, desde que ele tenha idade igual/menor que 10 anos.

Dado esse comportamento, vejamos um teste que valida o oposto: se o paciente tem mais de 10 anos, nada de brindes para ele (assumam que este teste está passando/verde):

public void Nao_deve_dar_brinde_para_pacientes_maiores_de_dez_anos()
{
  paciente = new PacienteBuilder().Build();

  brindes = sut.ObterBrindesPara(paciente);

  Assert.Empty(brindes);
}

O que há de errado com esse teste?

Ele deixa de mostrar uma informação crucial para a validação do seu resultado: a idade do paciente. Esse dado está implícito, “setado” por default no builder e, convenientemente, é uma idade maior que 10. Com isso:

  • Fica difícil racionalizar o teste. Um desenvolvedor lendo esse código teria que saber de cabeça como o builder está configurado ou ter que ir até o código do builder para buscar a informação.
  • O teste fica frágil. Isso pode ser mais improvável, mas, ainda assim, imaginem um desenvolvedor alterando o builder para retornar outro valor (uma idade de 5) ou, pior, um valor randômico! Esse teste – e talvez centenas deles – quebraria por conta dessa modificação no builder, o que não faz o menor sentido!

Alguém poderia dizer: “Ah mas o nome do teste já explica o cenário” ou “poderia deixar explícito no nome da variável ‘paciente'”. De fato. Porém, dizer que algo é alguma coisa, não a faz ser esta coisa, certo? Os problemas acima continuariam, já que você só teria que acreditar com fé que o setup do teste (no caso, o builder) está configurado corretamente.

Portanto, deixe os dados importantes para a validação do resultado do teste explícitos no setup do teste. É barato fazer isso, então por que não fazer em primeiro lugar?

// ...........
paciente = new PacienteBuilder().ComIdade(11).Build();
// ...........

PROBLEMA 2: DADOS DEMAIS

Vejamos o mesmo teste, escrito da seguinte forma:

public void Nao_deve_dar_brinde_para_pacientes_maiores_de_dez_anos()
{
  paciente = new PacienteBuilder()
                 .ComNome("Jose da Silva")
                 .ComIdade(11)
                 .ComStatus(Status.Ativo)
                 .ComCPF("12345678900")
                 .ComEndereco(new EnderecoBuilder().Build())
                 .Build();

  brindes = sut.ObterBrindesPara(paciente);

  Assert.Empty(brindes);
}

E agora? O que temos de errado?

Ao contrário do tópico anterior, agora temos dados demais no teste. Esses dados – nome, status, CPF e endereço – não exercem nenhuma influência no comportamento da SUT e no resultado do teste. Com isso:

  • O teste fica poluído. O que teria poucas linhas, pode ficar bem maior agora.

E ainda temos os mesmos 2 problemas do caso anterior:

  • Difícil de racionalizar. Que diferença faz o status? Será que faz? E se fosse inativo? E os outros atributos?
  • Teste frágil. Agora temos um alto acoplamento do código de produção (no caso, o paciente) com o teste. E se um paciente não precisar mais de um atributo “endereço”? Esse simples refactoring impactaria este teste sem necessidade, já que ele sequer deveria depender deste atributo!

Portanto, deixe os dados irrelevantes para a validação do resultado do teste de fora do setup do teste. Isso é ainda mais barato: é não fazer (ou remover)!

CONCLUSÃO

Os problemas mostrados neste artigo são criados por desconhecimento dos desenvolvedores quanto ao propósito de um Test Data Builder e, muitas vezes, replicados em diversos testes graças ao CTRL-C/CTRL-V de um teste para outro.

O teste demonstrado foi bem simples, mas em uma suíte real de testes, temos testes mais complexos, que podem conter diversos builders, o que só potencializa os problemas.

No entanto, vimos que a solução é extremamente simples e barata: com pequenos ajustes nos builders, podemos deixar nossos testes mais limpos, compreensíveis e robustos.

Recapitulando, “a regra é clara”:  o dado é relevante para a asserção do teste? Sim => deixe-o explícito. Não => deixe-o implícito.

Era isso! Dúvidas, sugestões, críticas, comentem!

4 comentários em “Test Data Builders: você está usando corretamente?

Adicione o seu

  1. E o que vocês pensam sobre testes que deixam dados importantes no “SetUp/Before” na suite de teste não sendo suficiente olhar apenas o teste em questão, mas tendo que buscar as informações relevantes no “SetUp/Before” mesmo que seja reaproveitada em mais de um teste.

    Será que não temos os mesmos problemas abordados de dificuldade para racionalizar e de fragilidade no teste?

    Curtir

    1. E aí, Jorge, blz?
      Sim. Até falei brevemente sobre isso num dos artigos passados. Caberia até um novo “Como eu ODEIO setups de teste”. Hahahaha.

      Sejamos justos, há cenários em que eles caem bem, mas, na maioria das vezes, abusam deles: coloca-se TUDO de TODOS os testes do arquivo neles. Assim, é preciso subir pro setup com frequencia pra entender o contexto do teste.
      []s

      Curtir

      1. Concordo contigo, acho horrível ter que ficar indo toda hora para o SetUp, talvez o único cenário ideal de colocar coisas relevantes nele é quando *todos* os testes da suit utilizarem daquela informação, do contrário, creio ser uma boa prática deixar o que é relevante em cada teste.

        Curtir

Participe! Vamos trocar uma ideia sobre desenvolvimento de software!

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Google

Você está comentando utilizando sua conta Google. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s

Blog no WordPress.com.

Acima ↑

%d blogueiros gostam disto: