Back-End

5 jan, 2012

Realizando testes em seus aplicativos Django

Publicidade

Este artigo é uma continuação não-oficial dos tutoriais que fiz para a documentação do Django. Eu tentei escrever no mesmo estilo da documentação do framework e espero que seja útil.

Testar é algo que acredito que todos nós podemos fazer melhor, e eu certamente poderia fazer melhor testando meu código às vezes.

Se você não tem a app final da parte quatro da documentação, mas não quer fazer tudo de novo, você pode pegar do meu github.O código adicionado neste artigo também está disponível no branch master do repositório.

Este artigo começa de onde a quarta parte da documentação terminou. Estamos continuando o aplicativo de pesquisa para web, e iremos focar em testá-lo e provar que ele funciona como esperado.

O ambiente de teste

Primeiro vamos dar uma olhada em como você faz os testes. Certifique-se de que você esteja no diretório mysite e execute o comando python manage.py test. Você verá algo parecido com isso:

Creating test database...  
Creating table auth_permission
Creating table auth_group
Creating table auth_user
Creating table auth_message
Creating table django_admin_log
Creating table django_content_type
Creating table django_session
Creating table django_site
Creating table polls_poll
Creating table polls_choice
Installing index for auth.Permission model
Installing index for auth.Message model
Installing index for admin.LogEntry model
Installing index for polls.Choice model
...................................
------------------------------------------------------------------
Ran 35 tests in 0.565s

OK
Destroying test database...

Você executou com sucesso a suíte de teste para o projeto. Se seu output não se parece com isso e parece que alguns dos testes falharam, você deve tirar um momento para voltar e se certificar de que seu projeto corresponda ao final da parte 4.

Quando você roda a suíte de testes, o Django cria um novo banco de dados de teste, sincroniza suas aplicações e carrega quaisquer fixtures para o banco de dados. Cada teste é então executado de cada vez e “amarrado” dentro de uma transação do banco de dados, de modo que ele pode ser revertido quando cada teste for completado. No final do teste, o Django destrói o banco de dados de testes para você.

O que acabamos de testar? O manage.py test roda todos os testes para cada uma das aplicações na sua configuração INSTALLED_APPS. Todas as aplicações django.contrib são enviadas com testes, e as suas também devem. Isso é ótimo, pois podemos usar isso para testar se todas as aplicações contrib estão configuradas corretamente.

Execute o mesmo comando novamente, mas inclua a flag de verbose, de modo que possamos ter mais informações sobre o que de fato aconteceu. Para fazer isso, execute o comando python manage.py test -v 2. Haverá mais output dessa vez, mostrando cada um dos testes que foram carregados com o resultado do teste.

O que é um teste?

Com o Django (e o Python em geral), existem duas maneiras principais de escrever testes para a suíte de testes dos seus projetos: doctests e unit tests. No Django, ambos usam os módulos padrão do Python doctest e unittest. Doctests são escritos em docstrings do Python, e uma unit test é definida com classes. Bons exemplos genéricos e explicações podem ser encontradas em ambas documentações do Django aqui e aqui. Se você não está certo sobre como usar, leia isto.

Para ser sucinto, vamos focar em unit testes neste artigo.

Escrevendo um teste

Na parte um da documentação, foi notado que quando você criava suas aplicações de pesquisa, o arquivo tests.py era criado. Os mais aventurosos de vocês terão dado uma olhada e visto um exemplo simples (mas redundante) de unit test e doctest. O executor de teste do Django por padrão roda quaisquer testes que você cria no tests.py no pacote de aplicação. Isso é parecido com a detecção automática das configurações do administrador nos arquivos admin.py.

Para somente rodar os testes para uma aplicação específica, execute o comando python manage.py test polls. Isso irá rodar os dois testes padrão que estão presentes nas aplicações poll que criarmos mais cedo neste artigo.

Delete os conteúdos do arquivo tests.py e adicione o seguinte caso de teste.

from datetime import datetime

from django.test import TestCase

from mysite.polls.models import Poll

class PollTest(TestCase):

def setUp(self):
question="What is your favourite colour?"
now = datetime.now()
self.poll = Poll.objects.create(question=question, pub_date=now)
self.poll.choice_set.create(choice="Red", votes=0)
self.poll.choice_set.create(choice="Bue", votes=0)
self.poll.choice_set.create(choice="Green", votes=0)

def test_models(self):
self.assertEqual(self.poll.choice_set.all().count(), 3)

Nesse caso de teste, o método setUp cria uma nova pesquisa e adiciona três escolhas a ela. Lembre-se de que como o executor de testes cria seu próprio banco de dados, isso ainda não são dados. O setUp é chamado no início de cada teste definido dentro das suas classes de casos testes. O teste verifica que o número de escolhas na pesquisa criada é igual a 3. Um teste é um método que começa com test_ e é uma propriedade de classe que estende TestCase.

Tente brincar com isso e fazer com que o teste falhe mudando o número ou adicionando/removendo as escolhas. O teste também pode terminar com um resultado de erro se existir alguma exceção não detectada, o que pode ser feito ao adicionar Poll.objects.get(pk=2) como se não existisse uma pesquisa com essa id. Vale a pena se familiarizar com os diferentes resultados possíveis do teste.

Ao executar seus testes rodando python manage.py test polls, o output mostra um único . para cada teste que passe. Se um teste falha, um F será exibido e, se houver um erro, E será exibido. Uma falha é quando uma das asserções falha, e um erro acontece quando existe uma exceção não detectada enquanto o teste é executado.

Vamos adicionar outro caso de teste que faz algo mais útil.

# ...
from django.test import Client

from mysite.polls.models import Poll, Choice

class PollTest(TestCase):

# ...

def test_voting(self):
c = Client()
# Perform a vote on the poll by mocking a POST request.
response = c.post('/polls/1/vote/', {'choice': '1',})
# In the vote view we redirect the user, so check the
# response status code is 302.
self.assertEqual(response.status_code, 302)
# Get the choice and check there is now one vote.
choice = Choice.objects.get(pk=1)
self.assertEqual(choice.votes, 1)

Nesse exemplo, nós usamos o cliente de teste do Django. Ao usar o cliente, somos capazes de simular pedidos sem precisar de um servidor, pois o objeto requerido está mocked, e o view é invocado com um pedido mock. Nesse teste, criamos um pedido de post que mocks um voto na pesquisa e então checa ambos o status_code da resposta (para checar se fomos redirecionados) e verifica se o número de votos aumentou.

Desenvolvimento orientado para testes

O desenvolvimento orientado para testes é a prática de escrever testes que falham e mostrar o que o sistema deveria fazer, e então escrever ou mudar o código para fazer o teste (s) passar (rem).

Em seguida, vamos adicionar um pouco de Ajax à nossa aplicação. Para fazer isso, queremos ser capazes de chamar o voto com um pedido Aja e receber uma informação simples com que possamos trabalhar, em vez de uma resposta html completa. Primeiro, iremos escrever o teste para como queremos que isso funcione. Queremos que o sistema retorne ‘1’ para um voto válido e ‘0’ para um voto inválido na resposta http.

class PollTest(TestCase):

# ...

def test_ajax_vote(self):

c = Client()

# Extra parameters to make this a Ajax style request.
kwargs = {'HTTP_X_REQUESTED_WITH':'XMLHttpRequest'}

# A valid vote
response = c.post('/polls/1/vote/', {'choice': '1',}, **kwargs)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, '1')

# A invalid vote - choice doesn't exist
response = c.post('/polls/1/vote/', {'choice': '10',}, **kwargs)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, '0')

# An invalid vote - poll doesn't exist
response = c.post('/polls/2/vote/', {'choice': '1',}, **kwargs)
self.assertEqual(response.status_code, 404)

Adicione isso a seus testes e então rode a suíte de testes. Você deve ter um output parecido com isto:

F..
===================================================================
FAIL: test_ajax_vote (mysite.polls.tests.PollTest)
-------------------------------------------------------------------
Traceback (most recent call last):
File "/mysite/polls/tests.py", line 41, in test_ajax_vote
self.assertEqual(response.status_code, 200)
AssertionError: 302 != 200

-------------------------------------------------------------------

Nós não atualizamos nossa view, então em vez de retornar algo útil para o pedido Ajax, o servidor retornou um redirecionamento para a página de resultados. Isso significa que o teste falhou no primeiro obstáculo quando checamos o código de resposta.

Agora o que precisamos fazer é atualizar o código na view de votos para garantir que os testes passem. Mude sua view para que ela corresponda ao seguinte:

def vote(request, poll_id):
p = get_object_or_404(Poll, pk=poll_id)
try:
selected_choice = p.choice_set.get(pk=request.POST['choice'])
except (KeyError, Choice.DoesNotExist):
# bad vote, return '0'
if request.is_ajax():
return HttpResponse("0")
# Redisplay the poll voting form.
return render_to_response('polls/poll_detail.html', {
'object': p,
'error_message': "You didn't select a choice.",
})
else:
selected_choice.votes += 1
selected_choice.save()
# vote saved, return '1'
if request.is_ajax():
return HttpResponse("1")
# Always return an HttpResponseRedirect after successfully dealing
# with POST data. This prevents data from being posted twice if a
# user hits the Back button.
return HttpResponseRedirect(reverse('poll_results', args=(p.id,)))

Execute novamente o teste, e ele agora deverá passar. Você escreveu com sucesso seus primeiros testes para o aplicativo de pesquisas (poll) e até fez um pouco de desenvolvimento orientado para testes.

O que vem em seguida e leitura complementar

Agora você deve ter uma boa compreensão do básico de testes e esperançosamente uma melhor ideia de como testar suas aplicações. Os testes são realmente bastante simples na maior parte do tempo, você simplesmente escreve um pouco mais de código para garantir que o código que você tem de fato funciona – não tem preço quando você entre na rotina e tem uma suíte de testes compreensiva.

Aqui estão alguns recursos em que você deveria dar uma olhada para tomar o próximo passo.

?

Texto original disponível em http://dougalmatthews.com/articles/2010/jan/20/testing-your-first-django-app/