{
  "path": "/tdd-com-python-e-flask.html",
  "site": "at://did:plc:3272gdrjsuikiff7qsgokgas/site.standard.publication/3mjaxtes2yf2v",
  "tags": [
    "pt",
    "Python",
    "Flask",
    "Backend"
  ],
  "$type": "site.standard.document",
  "title": "TDD com Python e Flask",
  "publishedAt": "2016-03-14T00:00:00.000Z",
  "textContent": "_Post originalmente publicado no Python Club._\n\n  \n\n> Baseado na palestra que ofereci no encontro do Grupy-SP, em 12 de março de 2016. O código dessa atividade está disponível no meu GitHub.\n\nA ideia desse exercício é introduzir a ideia de _test driven development_ (TDD) usando Python e Flask — digo isso pois a aplicação final desse “tutorial” não é nada avançada, tampouco funcional. E isso se explica por dois motivos: primeiro, o foco é sentir o que é o _driven_ do TDD, ou seja, como uma estrutura de _tests first_ (sempre começar escrevendo os testes, e não a aplicação) pode guiar o processo de desenvolvimento; e, segundo, ser uma atividade rápida, de mais ou menos 1h.\n\nEm outras palavras, não espere aprender muito de Python ou Flask. Aqui se concentre em sentir a diferença de utilizar um método de programar. Todo o resto é secundário.\n\n1. Preparando o ambiente\n\nRequisitos\n\nPara esse exercício usaremos o Python versão 3.5.1 com o framework Flask versão 0.10.1. É recomendado, mas não necessário, usar um virtualenv.\n\n> Como o código é bem simples, não acho que você vá ter muitos problemas se utilizar uma versão mais antiga do Python (ou mesmo do Flask). Em todo caso, em um detalhe ou outro você pode se deparar com mensagens distintas se utilizar o Python 2.\n\nVocê pode verificar a versão do seu Python com esse comando:\n\nDependendo da sua instalação, pode ser que você tenha que usar python3 ao invés de python — ou seja, o comando todo deve ser python3 --version. O resultado deve ser esse:\n\nE instalar o Flask assim:\n\nO pip é um gerenciador de pacotes do Python. Ele vem instalado por padrão nas versões mais novas do Python. Dependendo da sua instalação, pode ser que você tenha que usar pip3 ao invés de pip — ou seja, o comando todo deve ser pip3 install Flask. Com esse comando ele vai instalar o Flask e qualquer dependência que o Flask tenha:\n\nArquivos \n\nVamos usar, nesse exercício, basicamente 2 arquivos:\n\n app.py: onde criamos nossa aplicação web;\n tests.py: onde escrevemos os testes que guiarão o desenvolvimento da aplicação, e que, também, garantirão que ela funcione.\n\n2. Criando a base dos testes\n\nNo arquivo tests.py vamos usar o módulo unittest, que já vem instalado por padrão no Python.\n\nCriaremos uma estrutura básica para que, toda vez que esse arquivo seja executado, o unittest se encarregue de encontrar todos os nossos testes e rodá-los.\n\nVamos começar escrevendo com um exemplo fictício: testes para um método que ainda não criamos, um método que calcule números fatoriais. A ideia é só entender como escreveremos testes em um arquivo (tests.py) para testar o que escreveremos no outro arquivo (app.py).\n\nA estrutura básica a seguir cria um caso de teste da unittest e, quando executada, teste nosso método fatorial(numero) para todos os números de 0 até 6:\n\nSe você conhece um pouco de inglês, pode ler o código em voz alta, ele é quase auto explicativo: importamos o módulo _unittest_ (linha 1), criamos um objeto que é um caso de teste do método fatorial (linha 4), escrevemos um método de teste (linha 6) e esse método se assegura de que o retorno de fatorial(numero) é o resultado que esperamos (linhas 5 a 11).\n\nAgora podemos rodar os testes assim:\n\nVeremos uma mensagem de erro, NameError, pois não definimos nossa função fatorial(numero):\n\nTudo bem, a ideia não é brincar com matemática agora. Mas vamos criar essa função lá no app.py só para ver como a gente pode “integrar” esses dois arquivos — ou seja, fazer o tests.py testar o que está em app.py.\n\nVamos adicionar essas linhas ao app.py:\n\nE adicionar essa linha no topo do tests.py:\n\nAgora, rodando os testes vemos que a integração entre app.py e tests.py está funcionando:\n\nÓtimo. Chega de matemática, vamos ao TDD com Flask, um caso muito mais tangível do que encontramos no nosso dia-a-dia.\n\n3. Primeiros passos para a aplicação web\n\nCriando um servidor web\n\nComo nosso foco é começar uma aplicação web, podemos descartar os testes e o método fatorial que criamos no passo anterior. Ao invés disso, vamos escrever um teste simples, para ver se conseguimos fazer o Flask criar um servidor web.\n\nDescarte tudo do tests.py substituindo o conteúdo do arquivo por essas linhas:\n\nEsse arquivo agora faz quatro coisas referentes a nossa aplicação web:\n\n1. Importa o objeto meu_web_app (que ainda não criamos) do nosso arquivo app.py;\n1. Cria uma instância da nossa aplicação web específica para nossos testes (é o método meu_web_app.test_client(), cujo retorno batizamos de app);\n1. Tenta acessar a “raíz” da nossa aplicação — ou seja, se essa aplicação web estivesse no servidor pythonclub.com.br estaríamos acessando http://pythonclub.com.br/.\n1. Verifica se, ao acessar esse endereço, ou seja, se ao fazer a requisição HTTP para essa URL, temos como resposta o código 200, que representa sucesso.\n\nOs códigos de status de requisição HTTP mais comuns são o 200 (sucesso), 404 (página não encontrada) e 302 (redirecionamento) — mas a lista completa é muito maior que isso. \n\nDe qualquer forma não conseguiremos rodar esses testes. O interpretador do Python vai nos retornar um erro:\n\nEntão vamos criar o objeto meu_web_app lá no app.py. Descartamos tudo que tínhamos lá substituindo o contéudo do arquivo por essas linhas:\n\nApenas estamos importando a classe principal do Flask, e criando uma instância dela. Em outras palavras, estamos começando a utilizar o framework.\n\nE agora o erro muda:\n\nImportamos nosso meu_web_app, mas quando instanciamos o Flask temos um problema. Qual problema? O erro nos diz: quando tentamos chamar Flask() na linha 3 do app.py está faltando um argumento posicional obrigatório (_missing 1 required positional argument_). Estamos chamando Flask() sem nenhum argumento. O erro ainda nos diz que o que falta é um nome (_import_name_). Vamos batizar nossa instância com um nome:\n\nE agora temos uma nova mensagem de erro, ou seja, progresso!\n\n> Eu amo testes que falham! A melhor coisa é uma notificação em vermelho me dizendo que os testes estão falhando. Isso significa que eu tenho testes e que eles estão funcionando!\n> \n> — Bruno Rocha\n\nTemos uma aplicação web rodando, mas quando tentamos acessar a raíz dela, ela nos diz que a página não está definida, não foi encontrada (é o que nos diz o código 404).\n\nCriando nossa primeira página\n\nO Flask facilita muito a criação de aplicações web. De forma simplificada a qualquer método Python pode ser atribuída uma URL. Isso é feito com um decorador:\n\nAdicionando essas linhas no app.py, os testes passam:\n\nSe a curiosidade for grande, esse artigo (em inglês) explica direitinho como o Flask.route(rule, *options) funciona: Things which aren't magic - Flask and @app.route.\n\nPara garantir que tudo está certinho mesmo, podemos adicionar mais um teste. Queremos que a resposta do servidor seja um HTML:\n\nRodando os testes, veremos que agora temos dois testes. E ambos passam!\n\nEliminando repetições\n\nRepararam que duas linhas se repetem nos métodos test_get() e test_content_type()?\n\nPodemos usar um método especial da classe unittest.TestCase para reaproveitar essas linhas. O método TestCase.setUp() é executado ao iniciar cada teste, e através do self podemos acessar objetos de um método a partir de outro método:\n\nNão vamos precisar nesse exemplo, mas o método TestCase.tearDown() é executado ao fim de cada teste (e não no início, como a setUp()). Ou seja, se precisar repetir algum comando sempre após cada teste, a unittest também faz isso para você.\n\n4. Preenchendo a página\n\nConteúdo como resposta\n\nTemos um servidor web funcionando, mas não vemos nada na nossa aplicação web. Podemos verificar isso em três passos rápidos:\n\nPrimeiro adicionamos essas linhas ao app.py para que, quando executarmos o app.py (mas não quando ele for importado no tests.py), a aplicação web seja iniciada:\n\nDepois executamos o arquivo:\n\nAssim vemos no terminal essa mensagem:\n\nSe acessarmos essa URL no nosso navegador, podemos ver a aplicação rodando: http://127.0.0.1:5000/.\n\nE veremos que realmente não há nada, é uma página em branco.\n\nVamos mudar isso! Vamos construir o que seria uma página individual, mostrando quem a gente é. Na minha vou querer que esteja escrito (ao menos), meu nome. Então vamos escrever um teste para isso:\n\nFeito isso, teremos uma nova mensagem de erro nos testes:\n\nEssa mensagem nos diz que estamos comparando uma _string_ com um objeto que é de outro tipo, que é representado por _bytes_. Não é isso que queremos. Como explicitamente passamos para o teste uma _string_ com nosso nome, podemos assumir que é o self.response.data que vem codificado em _bytes_. Vamos decodificá-lo para _string_.\n\n> _Bytes precisam ser decodificados para string (método decode). Strings precisam ser codificados para bytes para então mandarmos o conteúdo para o disco, para a rede (método encode)._\n>\n> — Henrique Bastos\n\nAssim temos uma nova mensagem de erro:\n\nNossa página está vazia, logo o teste não consegue encontrar meu nome na página. Vamos resolver isso lá no app.py:\n\nAgora temos os testes passando, e podemos verificar isso vendo que temos o nome na tela do navegador.\n\nApresentando o conteúdo com HTML\n\nO Python e o Flask cuidam principalmente do back-end da apliacação web — o que ocorre “por trás dos panos” no lado do servidor.\n\nMas temos também o front-end, que é o que o usuário vê, a interface com a qual o usuário interage. Normalmente o front-end é papel de outras linguagens, como o HTML, o CSS e o JavaScript.\n\nVamos começar com um HTML básico, criando a pasta templates e dentro dela o arquivo home.html:\n\nSe a gente abrir essa página no navegador já podemos ver que ela é um pouco menos do que o que a gente tinha antes. Então vamos alterar nosso test_content() para garantir que ao invés de termos somente a _string_ com nosso nome na aplicação, tempos esse template renderizado:\n\nAssim vemos nossos testes falharem:\n\nCriamos um HTML, mas ainda não estamos pedindo para o Flask utilizá-lo. Temos nossa home.html dentro da pasta templates pois é justamente lá que o Flask vai buscar templ",
  "canonicalUrl": "https://cuducos.me/tdd-com-python-e-flask.html"
}