Só 1 assert por test case?

25 views
Skip to first unread message

Vinicius Assef

unread,
May 15, 2013, 1:32:54 PM5/15/13
to tdd-no-m...@googlegroups.com, django...@googlegroups.com
Pessoal, como vocês implementam as checagens de um método que deve ser testado?

Um único assert por test case sempre, como, aliás, é a recomendação
teórica ou na prática há casos que rola mais de 1 assert no test case?

Exemplo: pra testar se a URL '/index' carrega, preciso mandar
executá-la e testar:
a) o status http 200
b) qual template foi usado pra responder
c) algum conteúdo da página.

Esses asserts ficam em 3 test cases diferentes ou vocês colocam todos
esses asserts num único test case?

Como vocês fazem na prática?

--
Vinicius Assef

contatog...@gmail.com

unread,
May 15, 2013, 1:35:51 PM5/15/13
to django...@googlegroups.com
Eu faço um assert em cada método do test case.

_____________________________________________
Gilson Filho
Web Developer
http://gilsondev.com



--
Você está recebendo esta mensagem porque se inscreveu no grupo "Django Brasil" dos Grupos do Google.
Para cancelar a inscrição neste grupo e parar de receber seus e-mails, envie um e-mail para django-brasi...@googlegroups.com.
Para obter mais opções, acesse https://groups.google.com/groups/opt_out.



Andrews Medina

unread,
May 15, 2013, 1:41:25 PM5/15/13
to django...@googlegroups.com, tdd-no-m...@googlegroups.com
Olá,

2013/5/15 Vinicius Assef <vinic...@gmail.com>:
> Pessoal, como vocês implementam as checagens de um método que deve ser testado?
>
> Um único assert por test case sempre, como, aliás, é a recomendação
> teórica ou na prática há casos que rola mais de 1 assert no test case?
>

Depende do objetivo do teste. Eu gosto de fazer testes por `comportamento`.

> Exemplo: pra testar se a URL '/index' carrega, preciso mandar
> executá-la e testar:
> a) o status http 200
> b) qual template foi usado pra responder
> c) algum conteúdo da página.
>

Nesse caso é obvio que você tem 3 `comportamentos` que você espera.
Então eu faria 3 métodos em um TestCase.

Valeu!

--
Andrews Medina
www.andrewsmedina.com

Renne Rocha

unread,
May 15, 2013, 1:42:32 PM5/15/13
to django...@googlegroups.com, tdd-no-m...@googlegroups.com
Eu acho exagero fazer 1 método de teste para cada um desses asserts.

O que você está testando nesse caso, é o estado de um determinado objeto (nesse caso, um objeto response do Django). Para que você vai gerar 3 objetos com os seus atributos exatamente iguais para verificar suas propriedades isoladamente? Isso só vai fazer seu teste rodar mais devagar, pois você vai ter que fazer 3 requisições para obter exatamente a mesma coisa.


2013/5/15 Vinicius Assef <vinic...@gmail.com>

Renne Rocha

unread,
May 15, 2013, 1:46:17 PM5/15/13
to django...@googlegroups.com
  Andrews,

  Na minha opinião, nesse caso, o 'comportamento' esperado é que a página '/index/' seja carregada.

  Para eu considerar que a página foi carregada (o comportamento esperado), eu preciso verifica se um determinado objeto (o response) está em um determinado 'estado'.

  Esse estado é que o 'response.status_code == 200', 'response.content is not None' e 'templates in response.templates_used' (não lembro como os templates usados são armazenados no response :-P)

  Então eu faria os três asserts em um método só.


2013/5/15 Andrews Medina <andrew...@gmail.com>

Henrique Bastos

unread,
May 15, 2013, 2:15:51 PM5/15/13
to django...@googlegroups.com, django...@googlegroups.com
Vini,

Como regra geral, eu uso um assert por método de testes.

A reflexão é: se um assert falhar, vc quer q os outros sejam executados?

Em geral, executando os demais asserts te ajuda a perceber mais objetivamente onde está o problema (no contexto, no template, na query, etc).

Mas podem existir casos onde vc está verificando uma única coisa, mas com mais de um assert. Nesse caso, falhar um ou vários pode ser indiferente, então vc poderia ter eles em um test method só.

Na prática, eu observo que uso muito mais um assert por método, do que o contrario.

Uma outra coisa que faço é um testcase por propósito. O exemplo canônico pra demonstrar o que isso significa é um form de cadastro.

Imagine a pagina /inscricao/ que exibe um form. Ela tem 3 cenários possíveis: GET do form vazio, post com dados inválidos e post com dados validos. Neste caso, uso 3 TestCases diferentes.

Tenho observado q isso evita código de setUp espalhado em vários test methods fazendo micro ajustes para aquele teste especifico.

[]'s,
--
Henrique Bastos
+55 21 9618-6180
Twitter: henriquebastos

Andrews Medina

unread,
May 15, 2013, 2:26:49 PM5/15/13
to django...@googlegroups.com
Olá,

2013/5/15 Renne Rocha <re...@rennerocha.com>:
> Esse estado é que o 'response.status_code == 200', 'response.content is
> not None' e 'templates in response.templates_used' (não lembro como os
> templates usados são armazenados no response :-P)
>

O que quiz dizer é:

Se os asserts são comportamentos distintos, eu faria um método de
teste, mas se os asserts compões um comportamento faz mais sentido
fazer apenas um teste com vários asserts.

O ruim mesmo é você testar mais de um comportamento em um teste.

Outra coisa eu nunca testaria que o content não é None e sim testaria
se o content retornado é o que eu espero. :)

Vinicius Assef

unread,
May 15, 2013, 2:26:29 PM5/15/13
to django...@googlegroups.com
Na verdade, Renne, o exemplo testa mais do que apenas o estado do
response. Ele testa a resposta da aplicação, que pode ser analisada
sob vários aspectos, conforme eu ilustrei.

Por exemplo, se não vier um retorno 200, provavelmente você tem um
problema de url. Se não vier o template esperado, pode ser algum
problema de autenticação ou de interpretação de dados pela view. Ou,
ainda, se veio status 200 com o template correto, mas algum conteúdo
da página tá baleado, pode ser problema no template. Ou seja, acabam
sendo problemas independentes mesmo, com focos de solução bem
distintos.

É claro que olhando de longe, parece tudo uma coisa só, pois está tudo
agrupado numa única URL, mas são 3 micro-comportamentos que, juntos,
representam uma funcionalidade.

Outro exemplo, seria uma página com diversas áreas com informações
diferentes. Cada área dessa teria um (ou mais) métodos de teste para
verificar algum aspecto dela, mesmo que essas informações estejam numa
mesma página física.

Eu, particularmente, uso um assert por método de teste, por mais
verborrágico que seja. O benefício que eu vejo é que o nome do teste
que quebra me diz exatamente qual foi o problema, sem eu precisar
investigar o código do método de teste pra saber em qual assert
quebrou e o que isso significa pra aplicação.

Mas, como algumas coisas que fazemos sem trocar ideia, acabam virando
má prática, resolvi perguntar pra saber como outras pessoas fazem.

Esses dias eu encontrei esse link que lista anti-patterns [1] por
incidência. Adivinha qual foi o mais votado? ;-)

[1] http://stackoverflow.com/questions/333682/tdd-anti-patterns-catalogue

--
Vinicius Assef




2013/5/15 Renne Rocha <re...@rennerocha.com>:
> Você está recebendo esta mensagem porque se inscreveu no grupo "TDD no mundo
> real" dos Grupos do Google.
> Para cancelar a inscrição neste grupo e parar de receber seus e-mails, envie
> um e-mail para tdd-no-mundo-r...@googlegroups.com.

Vinicius Assef

unread,
May 15, 2013, 2:35:59 PM5/15/13
to django...@googlegroups.com
Henrique, é justamente assim que eu faço.

Entretanto, o cenário de form inválido pode precisar de mais de um
método de teste, principalmente quando existem validações cruzadas
entre campos, do tipo: campo B é obrigatório só se o campo A estiver
preenchido. Exemplos:
a) DDD + telefone
b) Endereço opcional, mas em campos separados (logradouro,
complemento, bairro, cidade, uf e cep)
c) Estado civil e nome do cônjuge

Assim, os cenários de teste multiplicam-se. Não vejo isso como um
problema. Aliás, problema é ter que ficar preenchendo formulário na
mão pra testar isso tudo, né? hehehe ;-)



2013/5/15 Henrique Bastos <henr...@bastos.net>:

Renne Rocha

unread,
May 15, 2013, 2:38:30 PM5/15/13
to django...@googlegroups.com
Acho que é mais uma questão de escolha pessoal e do que exatamente o teste está testando.

Eu me incomodo um pouco com milhares de métodos de testes e neste exemplo o assert que falhou me indicaria onde possivelmente está o problema:
- se o status não é 200, pode ser falta de autenticação por exemplo, ou que a URL não está definida
- se o template não é usado, deve ser no render ou meu template não está extendendo algo
- Sobre o conteúdo eu concordo com o Andrews, não entraria aqui mesmo, pois é melhor eu testar o conteúdo do conteúdo :-)

Outro caso que eu usaria vários asserts seria por exemplo, verificar se um email foi enviado corretamente. Não faria um teste para verificar se o email foi enviado, outro para verificar o assunto, outro para o conteúdo. O assert que falhou me dá uma indicação de onde está o erro.

Dois textos interessantes sobre esse assunto:

Acho que essa "regra" de um assert por teste segue na mesma linha de ter "100% de cobertura" de testes. Quanto mais próximo disso estamos, melhor, mas não deve ser um objetivo buscado a ferro e fogo e sim conseqüência natural da maneira como desenvolvemos.


2013/5/15 Vinicius Assef <vinic...@gmail.com>

Francisco Souza

unread,
May 15, 2013, 2:39:45 PM5/15/13
to django...@googlegroups.com
2013/5/15 Vinicius Assef <vinic...@gmail.com>:
> Henrique, é justamente assim que eu faço.
>
> Entretanto, o cenário de form inválido pode precisar de mais de um
> método de teste, principalmente quando existem validações cruzadas
> entre campos, do tipo: campo B é obrigatório só se o campo A estiver
> preenchido. Exemplos:
> a) DDD + telefone
> b) Endereço opcional, mas em campos separados (logradouro,
> complemento, bairro, cidade, uf e cep)
> c) Estado civil e nome do cônjuge
>
> Assim, os cenários de teste multiplicam-se. Não vejo isso como um
> problema. Aliás, problema é ter que ficar preenchendo formulário na
> mão pra testar isso tudo, né? hehehe ;-)

https://en.wikipedia.org/wiki/Keyword-driven_testing

--
~f

Andrews Medina

unread,
May 15, 2013, 2:41:34 PM5/15/13
to django...@googlegroups.com
Olá,

2013/5/15 Vinicius Assef <vinic...@gmail.com>:
> Por exemplo, se não vier um retorno 200, provavelmente você tem um
> problema de url.

Nesse caso você deveria ter um teste para verificar se a url está indo
para onde deveria.

Tem que tomar cuidado porque um micro comportamento isolado (sem
contexto), em vez de facilitar a leitura, pode dificultar o
entendimento.

Vamos dizer que tu fez um recurso REST para criar um conteudo e o
retorno desse recurso é um 200, com o content "success".

Se você tiver dois testes:

def test_status_code_should_be_200():
response = post('myurl')
assert 200 == response.status_code

def test_content_should_be_success():
response = post('myurl')
assert "success" == response.content

O que aconteceria, se alguém refatorar o recurso para retornar um 204
(no content)?

Nesses casos, micro 'comportamentos' acabam, não testando o
comportamento real (que era criar um conteúdo) e ainda na refatoração
pode gerar testes que não testam nada.

O que acho ideal é você garantir um comportamento no teste. Agora com
o tempo, equipe, projeto, negócio você vai aprendendo, o que é um
comportamento.

Henrique Bastos

unread,
May 15, 2013, 2:45:38 PM5/15/13
to django...@googlegroups.com
Vini,

Nesse caso a interface do form é meramente um form.isvalid().

Eu gosto de isolar o form e testar sua lógica de validação unitariamente, para garantir que o form se comporte de acordo, independente da entrada.



--
Henrique Bastos

Aprenda Python e Django na Prática!

+55 21 9618-6180


2013/5/15 Renne Rocha <re...@rennerocha.com>

Renne Rocha

unread,
May 15, 2013, 2:48:11 PM5/15/13
to django...@googlegroups.com
@Vinicius,

Escrevi a um tempo como faço meus testes de formulário: http://rennerocha.com/2013/01/como-eu-testo-formularios-no-django/

Seria legal ter mais opiniões e sugestões de maneiras melhores :-)

@Henrique,

Você cria uma classe para formulário vazio, outra para formulário válido e outra para inválido? No formulário vazio, você faz um método checando o erro para cada um dos campos obrigatórios? Teria um exemplo disso?



2013/5/15 Vinicius Assef <vinic...@gmail.com>

Andrews Medina

unread,
May 15, 2013, 2:57:12 PM5/15/13
to django...@googlegroups.com
Olá,

2013/5/15 Renne Rocha <re...@rennerocha.com>:
> @Vinicius,
>
> Escrevi a um tempo como faço meus testes de formulário:
> http://rennerocha.com/2013/01/como-eu-testo-formularios-no-django/
>

Legal o post, parabéns. Eu testo os forms de forma bem parecida, mas
faço mais testes unitários, verificando que ele tem os campos que eu
espero e etc.

Outra coisa que gosto é fazer o teste refletir a estrutura do meu
codigo, como se o teste fosse um espelho para o codigo. Um exemplo
seriam os testes do `pantera`:

https://github.com/globocom/pantera/blob/master/tests.py#L171
https://github.com/globocom/pantera/blob/master/pantera/chaos.py#L82

Vinicius Assef

unread,
May 15, 2013, 3:02:08 PM5/15/13
to django...@googlegroups.com
Excelente exemplo, Andrews!
Eu nunca tinha pensado num cenário assim.

2013/5/15 Andrews Medina <andrew...@gmail.com>:

Vinicius Assef

unread,
May 15, 2013, 3:04:20 PM5/15/13
to django...@googlegroups.com
Nessa eu boiei, Henrique.

Rola um exemplo disso? ;-)

2013/5/15 Henrique Bastos <henr...@bastos.net>:

Vinicius Assef

unread,
May 15, 2013, 3:08:58 PM5/15/13
to django...@googlegroups.com
Ótimo post, Renne. Bem prático.

Valeu.

2013/5/15 Renne Rocha <re...@rennerocha.com>:
Reply all
Reply to author
Forward
0 new messages