Você está na página 1de 24

Flask usando o Python3

FEBRUARY 19, 2018 / PATKENNEDY79@GMAIL.COM /


HT T P S : / / WWW . P A T RICK S O F T WA RE BL O G . COM / S TE P S -F O R -S T ART ING -A - NE W -F L A S K -P RO JE CT -US ING -P Y T HO N3 /

Introdução
Esta postagem do blog descreve minha abordagem atual para iniciar um novo

projeto do Flask. Esta postagem do blog abrange a execução de um aplicativo

"Hello World" simples usando as seguintes ferramentas:

• Python3

• Ambiente Virtual

• pip

• servidor de desenvolvimento Flask

Esta postagem de blog pressupõe o uso do Python 3.4 ou superior, já que essas

versões têm o módulo 'venv' embutido para criar ambientes virtuais e o pip vem

instalado automaticamente no ambiente virtual criado.

Estrutura
A maneira mais fácil de criar um aplicativo Flask é colocar tudo em um único
arquivo. Esta é a minha abordagem preferida para iniciar um novo projeto do
Flask, já que isso prova o ambiente que está sendo criado.
Comece criando um novo diretório e mudando para esse diretório:
$ mkdir flask_simple_application
$ cd flask_simple_application/

Neste ponto, você provavelmente verá que há pelo menos duas versões do python
disponíveis no seu sistema:
$ python --version
Python 2.7.10
$ python3 --version
Python 3.6.3

Eu recomendo fortemente o uso do Python3 para todos os novos projetos, pois ele
tem vários novos recursos e o suporte para o Python2 (legado do Python) está
terminando em 2020 (countdown clock).
Ambientes Virtuais
Até mesmo nos projetos mais simples, criar um ambiente virtual é uma ótima
ideia. Ambientes virtuais criam um ambiente isolado para cada projeto, o que
significa que cada projeto pode ter suas próprias dependências (ou seja, versão do
interpretador Python, pacotes python importados, etc.).
Os ambientes virtuais realmente se tornaram benéficos para mim quando eu estava
trabalhando em dois projetos simultâneos:

1. Um projetos existente que requer Python2, e precisa somente de pacotes


suportados pelo Python 2.
2. Um novo projeto onde eu queria usar o Python3
Ter ambientes virtuais separados para cada projeto permitiu configurar facilmente
qual versão do Python usar.
Como este projeto está usando o Python3, existe um módulo interno chamado
venv que pode ser usado para criar ambientes virtuais. Nota: o módulo venv foi
adicionado no Python 3.3.

Este é o comando para criar um novo ambiente virtual, no qual o diretório para
criar o ambiente virtual é especificado como "venv".

NOTA: certifique-se de usar "python3" em vez de "python" para garantir que o


Python3 seja usado no ambiente virtual.
$ python3 -m venv venv

Este comando criou um novo diretório chamado "venv", que contém os seguintes
itens para manter este projeto isolado dos outros projetos em seu sistema:

• Interpretador Python (Python 3.6.3 neste caso)


• Scripts para ativar e desativar o ambiente virtual

Para começar a usar o ambiente virtual que foi criado, ele precisa ser ativado:
$ source venv/bin/activate
(venv) $
Depois de ativar o ambiente virtual, o nome do ambiente virtual é exibido em seu
prompt de comando (o "(venv)" à esquerda do prompt).

Como o ambiente virtual está ativo, a execução do interpretador python usará o


Python3!
(venv) $ python --version
Python 3.6.3

Instalando pacotes com pip


Depois de criar o ambiente virtual para o seu aplicativo, é hora de começar a
instalar os pacotes do Python que você precisa usando o pip. Se você estiver
usando o Python 3.4 ou superior, o pip será instalado automaticamente em seu
ambiente virtual.
Comece instalando o framework Flask, que é o pacote "flask":
pip install flask

Este comando instala o pacote Flask mais todas as suas dependências (ou seja,
outros pacotes necessários ao Flask). Veja um exemplo de todos os pacotes que
foram instalados:
$ pip freeze
click==6.7
Flask==0.12.2
itsdangerous==0.24
Jinja2==2.10
MarkupSafe==1.0
Werkzeug==0.14.1

É uma boa ideia salvar os pacotes (incluindo números de versão) que estão sendo
usados no projeto:
$ pip freeze > requirements.txt

Isso pode ser realmente benéfico se você ou outra pessoa estiver iniciando com

este projeto e quiser instalar rapidamente todos esses pacotes com "pip install -r

requirements.txt".
Escrevendo Código Python
Agora é hora de realmente escrever algum código Python! No diretório de nível
superior criado, crie um novo arquivo chamado app.py com o seguinte conteúdo:
1 from flask import Flask
2
3 app = Flask(__name__)
4
5 @app.route('/')
6 def index():
return "Hello World!"
7

Este arquivo cria uma nova instância do aplicativo Flask e define uma única rota

que retorna a declaração “Hello World!”. Este é o aplicativo Flask mais básico,

mas mostrará que tudo foi configurado corretamente.


Executando o Servidor de Desenvolvimento
Para verificar se o app.py está funcionando corretamente, o servidor de
desenvolvimento do Flask que vem junto com a estrutura do Flask é uma ótima
maneira de testar seu aplicativo.

Primeiro, você precisa especificar onde o aplicativo Flask está definido (através da
variável de ambiente FLASK_APP). Então, você pode executar seu aplicativo
Flask com "flask run":
(venv) $ export FLASK_APP=app.py
(venv) $ flask run
* Serving Flask app "app"
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

Como o comentário indica, agora você pode navegar para http://127.0.0.1:5000/


(isso é mapeado para 'localhost' na maioria dos sistemas, para que você possa
alternativamente navegar para 'localhost: 5000') para visualizar seu aplicativo:
Estrutura da Aplicação do Flask
Neste ponto, o aplicativo Flask deve ficar assim:
$ tree -L 2
.
├── app.py
├── requirements.txt
└── venv
├── bin
├── ...

Conclusão
Esta postagem do blog forneceu as etapas que segui para criar um novo aplicativo
do Flask usando o Python 3. Em uma postagem de blog futura, descreverei como
gosto de estruturar um aplicativo do Flask.
Structuring a Flask Project
FEBRUARY 27, 2018 / PATKENNEDY79@GMAIL.COM / 0 COMMENTS
HTTPS://WWW .PATRICKSOFTW AREBLOG.COM/STRU CTURING-A-FLASK-PROJECT/

Introdução

O Flask oferece uma flexibilidade tão grande ao desenvolver um aplicativo

da Web, incluindo a estrutura do aplicativo. Os aplicativos de balão podem

variar de um único arquivo para projetos maiores com vários Blueprints.

Nesta postagem do blog, descreverei como gosto de estruturar um

aplicativo do Flask usando o Blueprints e uma função do Application

Factory.
Para referência, o projeto que criei como parte da publicação desta
postagem do blog está disponível no GitLab:
Flask User Management Example.
(https://gitlab.com/patkennedy79/flask_user_management_example)

Blueprints
Blueprints permiti que você organize de forma limpa o código-fonte do seu
aplicativo Flask. Cada Blueprint encapsula uma parte significativa da
funcionalidade em seu aplicativo. Por exemplo, eu gosto de ter um Blueprint
separado para lidar com o gerenciamento de usuários de um aplicativo Flask.
Eu gosto de criar um pacote separado (ou seja, um diretório que inclua o arquivo
__init__.py) para cada Blueprint.
Por exemplo, aqui está a estrutura do Flask User Management exemplo que
contém dois Blueprints (recipes and users “receitas e usuários”):
(venv) $ tree -L 5
.
├── README.md
├── instance
├── main.py
├── project
│ ├── __init__.py
│ ├── models.py
│ ├── recipes
│ │ ├── __init__.py
│ │ ├── routes.py
│ │ └── templates
│ │ └── recipes
│ │ └── index.html
│ ├── templates
│ │ └── base.html
│ └── users
│ ├── __init__.py
│ ├── forms.py
│ ├── routes.py
│ └── templates
│ └── users
│ ├── login.html
│ ├── profile.html
│ └── register.html
├── requirements.txt
└── venv
├── bin
│ ├── activate
| ├── ...

Dando uma olhada em um único Blueprint (usuários), ele encapsula todos os


componentes para lidar com os aspectos de gerenciamento de usuários do
aplicativo (definindo rotas, manipulando formulários, gerando modelos):
users
├── __init__.py
├── forms.py
├── routes.py
└── templates
└── users
├── login.html
├── profile.html
└── register.html

As subpastas para armazenar os arquivos de modelo podem parecer estranhas, mas


essa estrutura (… / blueprint_name / templates / blueprint_name /) é recomendada
na documentação do Flask. O diretório de modelos para cada Blueprint é
adicionado ao caminho de busca dos arquivos de modelo dentro do seu aplicativo
Flask. Com essa estrutura, você pode definir os modelos para cada Blueprint
dentro do Blueprint, mas ainda importar um modelo base (em… / project /
templates /) que seja compartilhado por todos os Blueprints. Com essa estrutura, o
arquivo de modelo para um Blueprint pode ser acessado usando "users /
login.html".
Para criar o Blueprint, um objeto da classe Blueprint é instanciado no arquivo
__init__.py da pasta Blueprint:
1
"""
2 The users Blueprint handles the user management for this application.
3 Specifically, this Blueprint allows for new users to register and for
4 users to log in and to log out of the application.
5 """
from flask import Blueprint
6 users_blueprint = Blueprint('users', __name__, template_folder='templates')
7
8 from . import routes
9
Esta instanciação do Blueprint especifica o nome do Blueprint (usuários) e
especifica a localização dos arquivos de modelo dentro do Blueprint. Além disso,
a última linha importa as rotas criadas em routes.py.

O users_blueprint que foi criado em __init__.py é então importado no arquivo


routes.py:
1 #################
2 #### imports ####
3 #################
4
from . import users_blueprint
5
Essa importação permite que as rotas que estão sendo criadas em routes.py sejam
especificadas com "@users_blueprint":
1 ################
2 #### routes ####
3 ################
4
5 @users_blueprint.route('/')
def index():
6 return render_template('index.html')
7
Depois de criar um Blueprint (e registrá-lo), você pode verificar se as rotas estão
sendo criadas com sucesso, verificando o mapeamento das URLs para funções
específicas. A melhor maneira de ver isso é abrir o interpretador Python com os
componentes principais do Flask carregados usando flask shell:
1
(venv) $ flask shell
2 >>> print(app.url_map)
3 Map([<Rule '/register' (OPTIONS, HEAD, GET, POST) -> users.register>,
4 <Rule '/profile' (OPTIONS, HEAD, GET) -> users.profile>,
5 <Rule '/logout' (OPTIONS, HEAD, GET) -> users.logout>,
<Rule '/login' (OPTIONS, HEAD, GET, POST) -> users.login>,
6 <Rule '/' (OPTIONS, HEAD, GET) -> recipes.index>,
7 <Rule '/static/<filename>' (OPTIONS, HEAD, GET) -> static>])
8
Isso mostra que as URLs estão sendo mapeadas corretamente para as funções nos

Blueprints.

Fábrica de Aplicação
A inicialização de um projeto do Flask requer os seguintes passos:

• Criando o aplicativo Flask como uma instância da classe Flask


• Configurando o aplicativo Flask
• Inicializando as extensões a serem usadas
• Registrando os Blueprints no projeto
Essas etapas podem ser reunidas em uma única função chamada de Application
Factory (uma implementação do Factory method pattern). Além de deixar claro
quais etapas precisam ser tomadas para inicializar um aplicativo Flask, a função
Application Factory permite que diferentes aplicativos Flask sejam criados com
diferentes configurações. Essa flexibilidade se torna muito útil durante o teste,
onde a configuração pode ser diferente do desenvolvimento ou da produção.
A Application Factory function é criado no arquivo… / project / __ init__.py.
Para começar, importe todos os módulos (incluindo as extensões do Flask) que
são necessários para o projeto Flask:
1 from flask import Flask
2 from flask_sqlalchemy import SQLAlchemy
3 from flask_login import LoginManager
from flask_bcrypt import Bcrypt
4
Em seguida, crie as instâncias de cada uma das extensões do Flask sem inicializá-
las:
1 #######################
2 #### Configuration ####
#######################
3
4 # Create the instances of the Flask extensions (flask-sqlalchemy,
5 flask-login, etc.) in
6 # the global scope, but without any arguments passed in. These instances
7 are not attached
# to the application at this point.
8 db = SQLAlchemy()
9 bcrypt = Bcrypt()
10 login = LoginManager()
11 login.login_view = "users.login"
Em seguida, a função real do Application Factory (create_app) é definida:
1
######################################
2 #### Application Factory Function ####
3 ######################################
4
5 def create_app(config_filename=None):
6 app = Flask(__name__, instance_relative_config=True)
7 app.config.from_pyfile(config_filename)
initialize_extensions(app)
8 register_blueprints(app)
9 return app
10
A função para inicializar cada extensão do Flask passa no aplicativo Flask para
cada extensão:
1 def initialize_extensions(app):
2 # Since the application instance is now created, pass it to each Flask
3 # extension instance to bind it to the Flask application instance (app)
4 db.init_app(app)
5 bcrypt.init_app(app)
login.init_app(app)
6 ...
7
Finalmente, todos os Blueprints são registrados com o aplicativo Flask:
1 def register_blueprints(app):
2 # Since the application instance is now created, register each Blueprint
3 # with the Flask application instance (app)
4 from project.recipes import recipes_blueprint
5 from project.users import users_blueprint
6
app.register_blueprint(recipes_blueprint)
7 app.register_blueprint(users_blueprint)
8
Depois de criar a função Application Factory, ela é usada no arquivo… / main.py:
1 from project import create_app
2
# Call the Application Factory function to construct a Flask application
3 instance
4 # using the standard configuration defined in /instance/flask.cfg
5 app = create_app('flask.cfg')

Agora o aplicativo Flask está pronto para ser executado usando:


1 (venv) $ export FLASK_APP=main.py
2 (venv) $ flask run
3 * Serving Flask app "main"
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
4
Estrutura geral
A estrutura geral para este projeto é mostrada abaixo:
(venv) $ tree -L 3
.
├── README.md
├── instance
├── main.py
├── project
│ ├── __init__.py
│ ├── models.py
│ ├── recipes
│ │ ├── __init__.py
│ │ ├── routes.py
│ │ └── templates
│ ├── templates
│ │ └── base.html
│ └── users
│ ├── __init__.py
│ ├── forms.py
│ ├── routes.py
│ └── templates
├── requirements.txt
└── venv
├── bin
├── ...

Os Blueprints são definidos em módulos separados na pasta… / project. A função

Application Factory é definida no arquivo… / project / __ init__.py. A aplicação

real do Flask é então criada em… / main.py.

Conclusão
Este post de blog descreveu como usar Blueprints e uma função Application
Factory para ajudar a estruturar um projeto Flask.

➔Os blueprints fornecem uma ótima ferramenta para organizar seu projeto em
grandes partes da funcionalidade.

➔Uma função Application Factory fornece um método limpo para criar o


aplicativo Flask.

Juntas, essas são as duas ideias principais que uso para estruturar um projeto do
Flask.
Testando uma aplicação de
flask usando pytest
MAY 2, 2018 / PATKENNEDY79@GMAIL.COM /

HTTPS://W WW .PATR ICKSOF TW AREBLOG.COM/ TES TING- A-FL ASK- APPL IC ATION-US ING-P YTES T/

Introdução
Recentemente comecei a usar pytest e é um framework de teste incrível para
python! Depois de ler o livro de Brian Okken intitulado “Python Testing with
pytest“, eu estava convencido de que queria começar a usar o pytest em vez do
módulo de teste unitário integrado que vem com o python. Nesta postagem do
blog, explicarei como testar um aplicativo do Flask usando o pytest.

pytest é uma estrutura de teste para python que você usa para gravar casos de
teste, mas também para executar os casos de teste. Depois de configurar sua
estrutura de teste, o pytest facilita muito a gravação de testes e oferece muita
flexibilidade para executar os testes. Usando o pytest, descobri que isso satisfaz
os principais aspectos de um bom ambiente de teste:

• testes são divertidos para escrever

• testes podem ser escritos rapidamente usando funções auxiliares (fixtures)

• testes podem ser executados com um único comando

• testes executados rapidamente

Para referência, o aplicativo Flask referenciado nesta postagem de blog pode ser
encontrado no GitLab:
https://gitlab.com/patkennedy79/flask_user_management_example
Estrutura
Em um projeto do Flask, gosto de manter todos os casos de teste em uma pasta
separada de "testes" que esteja no mesmo nível dos arquivos do aplicativo:
$ tree -L 3

├── main.py

├── project

├── requirements.txt

├── tests

│ ├── conftest.py

│ ├── functional

│ │ ├── __init__.py

│ │ └── test_users.py

│ ├── pytest.ini

│ └── unit

│ ├── __init__.py

│ └── test_models.py

└── venv

Além disso, eu realmente gosto de diferenciar entre testes de unidade e testes


funcionais (integração), dividindo-os como sub-pastas separadas dentro da pasta
"testes". Divulgação completa: Esta é a estrutura que Brian Okken usa em seu
livro (Python Testing with pytest) e eu realmente gosto dessa estrutura, pois ela
permite a flexibilidade de apenas executar testes de unidade ou apenas executar
testes funcionais.

Escrevendo testes de unidade


No Flask User Management project, há lógica no arquivo models.py que deve ser
testado para fornecer confiança de que ele funciona corretamente.

Ao executar o pytest, ele é capaz de executar automaticamente todos os arquivos


que iniciam com o teste * .py ou terminam com * test.py. Portanto, o arquivo para
testar a lógica em models.py deve ser chamado de test_models.py e deve ser
criado no diretório… / tests / unit_tests /. Aqui está o primeiro caso de teste para
escrever:
1 from project.models import User

4 def test_new_user():

5 """

GIVEN a User model


6
WHEN a new User is created
7
THEN check the email, hashed_password, authenticated, and role fields
8
are defined correctly
9
"""
10
user = User('patkennedy79@gmail.com', 'FlaskIsAwesome')
11 assert new_user.email == 'patkennedy79@gmail.com'

12 assert new_user.hashed_password != 'FlaskIsAwesome'

13 assert not new_user.authenticated

14 assert new_user.role == 'user'

Esse teste cria uma nova instância da classe User e verifica se o usuário foi
inicializado corretamente. Mais uma vez, estou roubando diretamente do livro de
Brian Okken (Python Testing with pytest) com o comentário para cada teste
unitário (GIVEN, WHEN, THEN sequence). Eu realmente gosto desta estrutura de
comentários para identificar o que o caso de teste está fazendo. Isso pode parecer
muito trabalho extra, mas realmente descobri que casos de teste bem comentados
facilitam muito a manutenção deles.

Para executar este caso de teste, navegue até o diretório de nível superior do seu
projeto e execute:
$ pytest
É isso aí! O pytest descobrirá todos os casos de teste para você e executará esses

testes. Super fácil!

Fixtures
Como alguém que trabalhou com o tipo xUnit de estruturas de teste, estou
familiarizado com a ideia de:

• Setup()
• …run the test case…
• Teardown()
Essa estrutura parece muito familiar, mas eu passei a preferir o conceito de
fixtures em pytest. As fixtures permitem uma flexibilidade maior do que o Setup ()
/ Teadown (), já que você pode fazer com que seu equipamento seja executado
com um escopo diferente:

• function

• class

• module

• session

Para essa situação em que uma única classe (User) está sendo testada, uma boa
opção para um fixture seria criar um novo User. Esse novo usuário pode ser criado
apenas uma vez e usado várias vezes em diferentes casos de teste.

Todos os aparelhos são adicionados ao… / tests / conftest.py:


1 import pytest

2 from project.models import User

5 @pytest.fixture(scope='module')

6 def new_user():
7 user = User('patkennedy79@gmail.com', 'FlaskIsAwesome')

8 return user

Este equipamento cria uma instância da classe User e a retorna para casos de teste
dentro do escopo do módulo a ser usado. Para usar este equipamento, altere o caso
de teste original para:
1 def test_new_user(new_user):

2 """

GIVEN a User model


3
WHEN a new User is created
4
THEN check the email, hashed_password, authenticated, and role fields
5
are defined correctly
6
"""
7
assert new_user.email == 'patkennedy79@gmail.com'
8 assert new_user.hashed_password != 'FlaskIsAwesome'

9 assert not new_user.authenticated

10 assert new_user.role == 'user'

O equipamento é executado antes deste caso de teste, porque é especificado como


um argumento (new_user). Para ilustrar o que este dispositivo está fazendo, vamos
mostrar a Pytest como ele está usando o equipamento usando o sinalizador

"–setup-show" (adicionamos dois casos de teste no test_models.py):


$ pytest --setup-show tests/unit/

=================================================== test session starts ==============

platform darwin -- Python 3.6.3, pytest-3.4.1, py-1.5.2, pluggy-0.6.0

rootdir: .../flask_user_management_example/tests, inifile: pytest.ini

collected 3 items

tests/unit/test_models.py

SETUP M new_user

unit/test_models.py::test_new_user (fixtures used: new_user).

unit/test_models.py::test_setting_password (fixtures used: new_user).


unit/test_models.py::test_user_id (fixtures used: new_user).

TEARDOWN M new_user

================================================ 3 passed in 2.02 seconds ============

Essa saída é ótima, porque mostra que o fixture (new_user) é executado antes de

qualquer um dos casos de teste em unit / test_models.py. Este acessório é então

usado por cada caso de teste em unit / test_models.py. Finalmente, o equipamento

é derrubado. Eu amo essa flexibilidade para fazer a configuração / desmontagem

padrão em diferentes escopos!

Escrevendo Testes Funcionais


Escrever testes funcionais é um pouco mais complicado, pois eles exigem mais
etapas de configuração. Em vez de mergulhar em um novo caso de teste, quero
começar escrevendo os acessórios que ajudarão na configuração dos testes
funcionais.

Os testes funcionais que vou criar estão testando como um usuário pode se
registrar, fazer login e sair. Para poder testar essa funcionalidade, precisamos de
um aplicativo Flask totalmente funcional em execução com um banco de dados. É
aqui que as fixture realmente brilham, na minha opinião ...

Aqui está a seqüência de como os testes funcionais devem ser executados:


• Criar um novo aplicativo Flask
• Inicializar um banco de dados
• … execute os testes funcionais…
• Destrua o banco de dados
• Pare o aplicativo Flask
Eu criei dois equipamentos em… / tests / conftest.py para implementar essa
sequência…

(1) Fixture para Criar o Aplicativo Flask


Um dos principais aspectos dessa seqüência é ter uma fábrica de aplicativos
que você pode usar para criar seu aplicativo Flask (veja meu post no blog
Structuring a Flask Application). A primeira fixture cria o aplicativo Flask:
1 @pytest.fixture(scope='module')

2 def test_client():

flask_app = create_app('flask_test.cfg')
3

4
# Flask provides a way to test your application by exposing the Werkzeug
5
test Client
6
# and handling the context locals for you.
7
testing_client = flask_app.test_client()
8

9
# Establish an application context before running the tests.
10
ctx = flask_app.app_context()
11 ctx.push()

12

13 yield testing_client # this is where the testing happens!

14

15 ctx.pop()

Este equipamento começa criando um novo aplicativo Flask através da função


create_app () e um arquivo de configuração customizado (flask_test.cfg). Em
seguida, é criado um cliente de teste que será usado nos testes funcionais para
responder a GETs e POSTs. Para que o aplicativo de frasco possa responder a
GETs e POSTs, o application context precisa ser empurrado para poder lidar com
os GETs e POSTs. Neste ponto, o "yield testing_client" é chamado para permitir
que todos os testes funcionais sejam executados com o cliente de teste que foi
criado neste dispositivo. Finalmente, o contexto do aplicativo é exibido para
limpar o ambiente de teste.

Configuração de teste

Um ponto importante a ter em conta é que os parâmetros especificados no arquivo


flask_test.cfg são realmente importantes, especialmente os seguintes:
# Bcrypt algorithm hashing rounds (reduced for testing purposes only!)
1
BCRYPT_LOG_ROUNDS = 4
2

3 # Enable the TESTING flag to disable the error catching during request

4 handling

5 # so that you get better error reports when performing test requests

6 against the application.

TESTING = True
7

8
# Disable CSRF tokens in the Forms (only valid for testing purposes!)
9
WTF_CSRF_ENABLED = False

O primeiro parâmetro (BCRYPT_LOG_ROUNDS) especifica o número de


rodadas de hashing a serem executadas ao fazer o hashing de senha dos usuários.
Esse número pode variar de 4 a 15, mas usar o valor mínimo para fins de teste
reduz significativamente o tempo de execução dos testes.

O próximo parâmetro (TESTING) é recomendado para testar um aplicativo Flask.

O último parâmetro (WTF_CSRF_ENABLED) está desabilitado, o que só deve


ser feito durante o teste. Esse parâmetro deve ser desativado para os testes serem
executados, mas CSRF a proteção é absolutamente crítica ao executar o aplicativo
real.

(2) Fixture para inicializar o banco de dados


A segunda fixture cria e inicializa o banco de dados:
@pytest.fixture(scope='module')
1
def init_database():
2
# Create the database and the database table
3
db.create_all()
4

5
# Insert user data
6 user1 = User(email='patkennedy79@gmail.com', plaintext_password=

7 'FlaskIsAwesome')

8 user2 = User(email='kennedyfamilyrecipes@gmail.com', plaintext_password=

9 'PaSsWoRd')

db.session.add(user1)
10
db.session.add(user2)
11

12
# Commit the changes for the users
13
db.session.commit()
14

15
yield db # this is where the testing happens!
16

17
db.drop_all()

Esta fixture cria o banco de dados (db.create_all ()) e adiciona dois usuários ao
banco de dados para usar durante o teste funcional. Mais uma vez, há uma
instrução "yield db" para permitir que os testes de função sejam executados ao
usar essa instância de banco de dados. Depois que todos os testes funcionais são
executados, o banco de dados é destruído (db.drop_all ()).

Agora que essas fixtures estão no lugar, é hora de escrever um teste funcional
(em… / tests / functional / test_users):
def test_home_page(test_client):
1
"""
2
GIVEN a Flask application
3
WHEN the '/' page is requested (GET)
4
THEN check the response is valid
5 """

6 response = test_client.get('/')

assert response.status_code == 200


7
assert b"Welcome to the Flask User Management Example!" in response.data
8
assert b"Need an account?" in response.data
9
assert b"Existing user?" in response.data
10

11

Esse teste funcional está solicitando a home page (no URL base de '/') e
verificando se o código de status retornado é válido (200) e o html retornado
contém as principais instruções de uma home page.

Esse teste funcional usa o cliente de teste para a solicitação GET, portanto, o
equipamento ‘test_client’ é especificado como um argumento. Como esse teste
funcional não está usando o banco de dados, o equipamento "init_database" não é
especificado.

Para executar este caso de teste individual com a execução do equipamento


explicada, execute:
$ pytest --setup-show tests/functional/test_users.py::test_home_page

=================================================== test session starts ==============

platform darwin -- Python 3.6.3, pytest-3.4.1, py-1.5.2, pluggy-0.6.0

rootdir: .../flask_user_management_example/tests, inifile: pytest.ini

collected 1 item

tests/functional/test_users.py

SETUP S test_client

functional/test_users.py::test_home_page (fixtures used: test_client).

TEARDOWN S test_client

================================================ 1 passed in 0.13 seconds ============

Aqui está um caso de teste mais complexo que verifica o login e, em seguida,
efetua logout:
1
def test_valid_login_logout(test_client, init_database):
2
"""
3
GIVEN a Flask application
4
WHEN the '/login' page is posted to (POST)
5
THEN check the response is valid
6
"""
7 response = test_client.post('/login',

8 data=dict(email='patkennedy79@gmail.com', passw

9 'FlaskIsAwesome'),

10 follow_redirects=True)

11 assert response.status_code == 200

assert b"Thanks for logging in, patkennedy79@gmail.com!" in response.data


12
assert b"Welcome patkennedy79@gmail.com!" in response.data
13
assert b"Flask User Management" in response.data
14
assert b"Logout" in response.data
15
assert b"Login" not in response.data
16
assert b"Register" not in response.data
17

18 """

19 GIVEN a Flask application

20 WHEN the '/logout' page is requested (GET)

21 THEN check the response is valid

"""
22
response = test_client.get('/logout', follow_redirects=True)
23
assert response.status_code == 200
24
assert b"Goodbye!" in response.data
25
assert b"Flask User Management" in response.data
26
assert b"Logout" not in response.data
27
assert b"Login" in response.data
28 assert b"Register" in response.data

29
Como esse teste funcional está efetuando login em um usuário, é necessário usar o
banco de dados para acessar se o usuário especificado for um usuário registrado.
Portanto, os fixtures "test_client" e "init_database" são especificados.

Para ilustrar o uso desses fixtures, aqui está o comando para executar todos os
testes funcionais com o uso dos fixtures explicados:
$ pytest --setup-show tests/functional/

=================================================== test session starts ==============

platform darwin -- Python 3.6.3, pytest-3.4.1, py-1.5.2, pluggy-0.6.0

rootdir: .../flask_user_management_example/tests, inifile: pytest.ini

collected 7 items

tests/functional/test_users.py

SETUP S test_client

functional/test_users.py::test_home_page (fixtures used: test_client).

functional/test_users.py::test_home_page_post (fixtures used: test_client).

functional/test_users.py::test_login_page (fixtures used: test_client).

SETUP S init_database

functional/test_users.py::test_valid_login_logout (fixtures used:

init_database, test_client).

functional/test_users.py::test_invalid_login (fixtures used: init_database,

test_client).

functional/test_users.py::test_valid_registration (fixtures used: init_databas

test_client).

functional/test_users.py::test_invalid_registration (fixtures used:

init_database, test_client).

TEARDOWN S init_database

TEARDOWN S test_client

================================================ 7 passed in 0.36 seconds ============

Essa saída é realmente impressionante, pois mostra que o pytest começa


executando o fixture de teste "test_client" para criar o aplicativo Flask. Os três
casos de teste que dependem apenas do fixture "test_client" são executados. Neste
ponto, os testes restantes também exigem o fixture "init_database", portanto, o
pytest executa este fixture e, em seguida, os casos de teste restantes. No final, os
dois fixture estão completos.

Esse uso de fixtures permite que a configuração do aplicativo Flask e do banco de


dados seja feita apenas uma vez no escopo ‘session’ e, em seguida, cada teste
funcional utiliza essa configuração.

Para finalizar o uso do pytest, aqui está o meu comando favorito para executar a
unidade e os testes funcionais:
$ pytest -v

Conclusão
Sim, o pytest é incrível! Esta postagem do blog mostra como usar o pytest para
testar um aplicativo Flask, mas ele apenas reflete a superfície do poder total do
pytest. Para continuar aprendendo sobre pytest, eu recomendo altamente o livro de
Brian Okken: Python Testing with pytest.

Você também pode gostar