quinta-feira, 8 de março de 2012

Melhorando a escrita dos testes em Django

Esses dias eu estava discutindo com o Avelino que é um saco escrever testes porque em toda maldita classe de testes eu preciso recriar os objetos, tipo assim:

class TesteDaMorte(TestCase):
    def setUp(self):
        # Aqui vem um bilhao de linhas pra criar
        # algumas instancias de objetos.
        # E nenhuma dessas linhas testa alguma 
        # coisa de fato

Eu argumentei que isso é um porre, e se repete várias vezes (código repetido nunca é bom), ai um belo dia o model recebe um novo campo e pronto, la vai você retardadamente reescrever todos os testes pra adicionar o novo campo na criação do objeto e não pra escrever os testes em cima dele. Eu acho esse esforço um desperdicio, mas ao mesmo tempo, sei que testes são extremamente importantes.

Aah, a documentação, nada como ler a documentação...

Bom, como meu índice de preguiça costuma ser representado em gráficos com o texto "MAX" no topo do eixo Y, eu decidi que não ia mais ficar escrevendo essa caralhada de código desnecessário em testes e saí em busca de alternativas.

Eu já tinha pensado em alguma hipoteses como heranças ou chamar setUp's de outros testes, mas não me agradou muito essas opções pelo que o Avelino vivia falando: "ah, mas os testes precisam ser independentes" (ou algo assim, não lembro exatamente o termo, mas era algo assim), e de fato, eu concordo, os testes precisam rodar sozinhos e serem capazes de se testarem a si mesmos isoladamente.

Depois pensei em outras alternativas que provavelmente ainda não existem implementadas, como definir dependencias de testes. Ou seja, não adianta você testar B, se A não passar. Mas como eu disse, não encontrei nenhuma documentação falando se isso existe, então ignorei logo (não tenho experiencia com testes o suficiente pra poder escrever uma solução desse porte).

Cansado de pensar em soluções, resolvi fazer o óbvio: fui procurar a solução na internet. Naturalmente a solução veio direto do site do Django na página de testes: Fixtures!

Quando definindo uma classe de testes, adicione um atributo na classe chamado "fixtures" e indicando quais fixtures você gostaria de importar antes de iniciar os testes. Assim:

class TesteDaMorte(TestCase):
    fixtures = ['tests/usuarios.json']
    def setUp(self):
        # Voce vai poder apagar essa funcao em 90% 
        # dos seus testes e ir direto ao que interessa

Dessa forma, antes de iniciar os testes (e antes mesmo de chamar a função setUp), o Django vai importar as fixtures do arquivo usuarios.json e vai criar os dados pra você.

Pra criar o arquivo de fixtures, você pode iniciar um novo syncdb em um banco de dados temporário, em sqlite mesmo se for o caso, inserir os dados la pelo admin (ou pelo shell) e executar o commando dumpdata do Django, mais ou menos assim:

python manage.py --indent=2 dumpdata auth.User profile.Profile > myapp/fixtures/tests/usuarios.json

Isso vai gerar o arquivo usuarios.json (desde que o diretório exista, claro) com os dados de User e Profile (supondo que seja assim que você o chamou). A ordem em que você adiciona eles no comando é relevante, é importante que as classes sejam especificadas por ordem de parentesco, ou seja, as classes pai aparecem antes das classes filhas (sendo pai=classe que tem o ID e filho=classe que tem a ForeignKey).

Com isso você pode focar em escrever o que importa de verdade: testes.

Nenhum comentário:

Postar um comentário