Django: Guia de testes
Transcrição
Django: Guia de testes
Django: Guia de testes Osvaldo Santana Neto ©2014 Osvaldo Santana Neto Tweet Sobre Esse Livro! Por favor ajude Osvaldo Santana Neto a divulgar esse livro no Twitter! O tweet sugerido para esse livro é: Acabei de comprar o ebook ”Django: Guia de testes” do @osantana: http://j.mp/djteste A hashtag sugerida para esse livro é #django-testes. Descubra o que as outras pessoas estão falando sobre esse livro clicando nesse link para buscar a hashtag no Twitter: https://twitter.com/search?q=#django-testes Para meus amores. Papai ama vocês. Conteúdo Créditos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Introdução . . . Django Tests . O Projeto . . Primeiro Teste . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 2 2 4 5 CONTEÚDO Créditos • Autor e Capa: Osvaldo Santana Neto • Foto da Capa: Fabrizio Sciami AVISO! Esse livro ainda está em processo de escrita, portanto, as informações aqui podem estar incorretas. Além disso a revisão de escrita (gramática, ortografia) será feita apenas na etapa final. É bem provável que você encontre erros graves de português antes disso. 1 Introdução Quando comecei a trabalhar com Django encontrei algumas dificuldades na criação de testes automatizados em meus projetos. Minhas dificuldades principais eram: • Fazer com que os testes unitários não manipulassem o banco de dados; • Escrever testes que executassem rápido; • Testar só o meu código e não o framework. Com o tempo eu me desapeguei de algumas crenças que tinha no desenvolvimento de testes unitários (ex. testes unitários não devem interagir com o banco de dados) e aceitei o “Jeito Django” de testar. Mas eu tive que aprender isso na marra porque não encontrava bons tutoriais oferecendo uma abordagem mais prática e com exemplos baseados em aplicações de verdade. Só encontrava exemplos muito básicos e completamente descolados do “mundo real”. Vamos trabalhar com o Django 1.7 mas os exemplos devem funcionar sem maiores problemas com o Django 1.6. Você perceberá que também uso Python 3 nos exemplos. Django Tests A distribuição padrão do Django trás quase tudo o que é necessário para testarmos nossas aplicações e por isso vou focar nessas ferramentas. Existe um bom conjunto de ferramentas e aplicações Django voltadas para o desenvolvimento de testes no site Django Packages. Organizando os testes Um projeto Django ideal tem as suas funcionalidades separadas em várias aplicações (applications). O modo padrão de organizar os testes de uma aplicação é criando módulos ou packages Python com nomes seguindo o formato test*. Usaremos o formato: app/tests/test_*.py. Dentro desses módulos você pode ter uma ou mais subclasses de um TestCase com métodos denominados test_* onde implementaremos os cenários de testes que precisamos. Executando os testes Para executar os testes de um projeto é só usar o comando manage.py test: Introdução 3 # Todos os testes do projeto $ ./manage.py test # Todos os testes da aplicação "reminder" $ ./manage.py test reminder # Todos os testes de um TestCase específico $ ./manage.py test reminder.tests.test_views.ViewTestCase # Apenas um teste $ ./manage.py test reminder.tests.test_views.ViewTestCase.test_home Dica 1 Durante o desenvolvimento de uma funcionalidade não é necessário executar o conjunto completo de testes da sua aplicação o tempo todo. Execute apenas aqueles testes relacionados diretamente com o que você está trabalhando. Deixe para executar todos os testes apenas quando você terminar a sua sessão de trabalho e antes de fazer commit/push para se certificar de que nada esteja quebrado. Dica 2 Crie um ambiente de integração continua (Continuous Integration - CI) que execute todos os testes do seu projeto sempre que alguém enviar código novo para o respositório de código. Esse tipo de ferramenta é extremamente útil. Tipos de TestCase A distribuição padrão do Django disponibiliza vários tipos de classes “TestCase” mas as principais são: django.test.TestCase Essa classe funciona de forma análoga à classe unittest.TestCase da biblioteca padrão do Python mas adiciona alguns facilitadores: 1. Cliente: uma instância de Client() no atributo self.client. Essa instância permite que a gente simule a execução de requisições HTTP em nossa aplicação. 2. Transações: durante a execução dos testes o Django controla a execução das transações para permitir que cada teste comece sempre em um cenário de banco de dados vazios. django.test.LiveServerTestCase Os conjuntos de testes que herdam dessa classe colocam o servidor de desenvolvimento do Django no ar para permitir a execução de testes de interação com ferramentas para testes funcionais como Selenium ou Splinter. Esse tipo de teste é bem difícil de manter e, com o tempo, eles passam a ser algo que mais atrapalha do que ajuda. Eu evito esse tipo de teste porque minhas aplicações geralmente não fazem uso muito intenso de engenharia de frontend (HTML/CSS/JS). Se o seu projeto usa muito JS, por exemplo, esse tipo de teste ganha mais importância. 4 Introdução O Projeto Para exemplificar o desenvolvimento guiado por testes vamor criar uma aplicação de agenda/calendário (calendar) em um projeto de projeto de PIM (Personal Information Manager - Gerenciador de Informações Pessoais). Essa aplicação deve ter as seguintes características: • • • • • • • Gerenciar Events (eventos) para determinado dia com hora de início e fim; Um evento precisa começar e terminar no mesmo dia; Se a hora de início e de término do evento não for informada ele terá duração de um dia inteiro; Não podemos permitir a criação de mais de um evento para o mesmo horário; O serviço precisa ser multiusuário (uma agenda por usuário); Um usuário não poderá ver a agenda de outro; O usuário receberá um email e um SMS (via Twilio) com a antecedência informada no evento. Iniciando Assumindo que você já tenha um ambiente virtual com Python 3 (venv) e com o Django 1.7 instalado. Vamos iniciar o nosso projeto: $ $ $ $ django-admin.py startproject pim cd pim ./manage.py startapp cal mkdir cal/templates/cal/ Nosso projeto deve ter uma estrutura parecida com essa: pim/ |-- cal/ | |-- migrations/ | | `-- __init__.py | |-- templates/ | | `-- cal/ | |-- __init__.py | |-- admin.py | |-- models.py | |-- tests.py | `-- views.py |-- pim/ | |-- __pycache__/ | |-- __init__.py | |-- settings.py | |-- urls.py | `-- wsgi.py `-- manage.py* <-- Testes O comando ./manage.py startapp já cria um módulo tests.py mas eu prefiro organizar meus testes de um modo que me permite separar um pouco mais as coisas 5 Introdução $ mkdir cal/tests/ $ touch cal/tests/__init__.py $ rm cal/tests.py A aplicação cal vai ficar assim: cal/ |-- migrations/ | `-- __init__.py |-- templates/ | `-- cal/ |-- tests/ | `-- __init__.py |-- __init__.py |-- admin.py |-- models.py `-- views.py Primeiro Teste Agora que temos a estrutura básica do nosso projeto vamos fazer um teste bem básico: verificar se a página inicial está funcionando. Vamos criar o primeiro teste: # cal/tests/test_views.py from django.test import TestCase class ViewTestCase(TestCase): def test_home(self): response = self.client.get("/") self.assertEqual(response.status_code, 200) # 200 OK E adicionaremos a aplicação cal na nossa lista de aplicações instaladas (INSTALLED_APPS) no arquivo pim/settings.py: # pim/settings.py INSTALLED_APPS = ( 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', Introdução 6 # Project applications 'cal', ) Agora estamos prontos para executar o nosso teste: $ ./manage.py test Creating test database for alias 'default'... F ====================================================================== FAIL: test_home (cal.tests.test_views.ViewTestCase) ---------------------------------------------------------------------Traceback (most recent call last): File "./pim/cal/tests/test_views.py", line 8, in test_home self.assertEqual(response.status_code, 200) # 200 OK AssertionError: 404 != 200 ---------------------------------------------------------------------Ran 1 test in 0.035s FAILED (failures=1) Destroying test database for alias 'default'... E como já era esperado o nosso teste está falhando, afinal, ainda não criamos a view nem roteamos a URL para nossa página home. Vamos implementar a nossa view: # cal/views.py from django.shortcuts import render def home(request): return render(request, "cal/home.html") Criar um template para ser renderizado: <!-- cal/templates/cal/home.html --> <html> <head> <title>Hello World!</title> </head> <body> <h1>Hello Django Tests!</h1> </body> </html> E, finalmente, rotear uma URL apontando para a nossa view: 7 Introdução # pim/urls.py from django.conf.urls import patterns, include, url from django.contrib import admin urlpatterns = patterns('', url(r'^$', 'cal.views.home', name='home'), url(r'^admin/', include(admin.site.urls)), ) # / -> home Pronto. Agora podemos executar o nosso teste novamente: $ ./manage.py test Creating test database for alias 'default'... . ---------------------------------------------------------------------Ran 1 test in 0.011s OK Destroying test database for alias 'default'... E tudo passou perfeitamente. Estamos prontos para continuar. Conclusão Quando a gente trabalha com Test-Driven Development esses passos se repetem o tempo todo: 1. 2. 3. 4. Criamos um teste; Executamos para garantir que o teste falha; Implementamos o código para que o teste passe; Refatoramos o código garantindo que os testes continuem passando. Dica Se você criou um teste novo e ele passa de primeira desconfie que ele está testando algo que já foi testado ou que a implementação do teste está incorreta.
Documentos relacionados
Komodo Edit – Customizando e utilizando com
Se não der pra ler, dentro do “Run” que é onde vai o programa, está digitado o seguinte comando: django-admin.py startproject %(ask:Digite o nome do Projeto:) Marque a opção “Add to Toolbox” Em Adv...
Leia maisDjango - Elton Minetto
Deu para perceber que cada modelo é uma classe em Python, o método __str__ foi definido para que o nome do objeto seja retornado. A classe interna chamada Admin que foi definida em cada classe sign...
Leia mais