(2014-11-05) [PythonBrasil] Testando com py.test e tox

28
Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini 2014-11-05 – Recife / PE Testando com py.test e tox Testando com py.test e tox Danilo de Jesus da Silva Bellini Twitter: @danilobellini http://pytest.org/ https://tox.readthedocs.org/ https://github.com/schlamar/pytest-cov http://nedbatchelder.com/code/coverage

description

Minicurso/tutorial sobre o py.test e o tox apresentado no Hotel Armação, Porto de Galinhas - Pernambuco, no dia 2014-11-05, durante a PythonBrasil[10]. Descrição do tutorial: ## Tipo Tutorial ## Trilha Tools & methodology ## Nível da audiência Intermediário ## Idioma Português ## Título Testando com py.test e tox ## Descrição Tutorial para o aprendizado sobre o uso prático do pacote py.test para realização de testes de diversos tipos. Tópicos: - Comportamento básico para coleta e execução de testes; - Testando exceções; - Testes parametrizados e seus usos com oráculos, testes aleatórios; - Cobertura de código com pytest-cov; - Testando apenas uma parte de uma suíte; - Integrando com doctests; - Criando fixtures personalizadas; - Mock/stub/fake/dummy com a fixture monkeypatch; - Testando warnings; - Usando tox para automatizar testes em múltiplos ambientes (e.g. Python 2 e 3); - Configurações (avançadas) para tempo de coleta e tempo de setup; - Usando skip, xfail e deseleção para testes que irão falhar; - Testes envolvendo arquivos temporários; O tutorial inclui uma breve discussão sobre o que pode ser testado, além de exemplos sobre comparação de ponto flutuante, aproximações e manutenção de resultados para testes que envolvam cálculo numérico ou dados científicos/multimídia. É necessário levar o computador com o Python instalado (2.7 ou 3.4, preferencialmente ambos), e com o pip (ou algo equivalente) disponível para instalação de pacotes. Pode-se instalar previamente os pacotes pytest, pytest-cov e tox. O tutorial é voltado para quem já sabe programar em Python. Embora não seja fundamental, é de grande ajuda ter o conhecimento prévio sobre assuntos como gerenciadores de contexto, exceções, decorators, orientação a objetos e arquivos setup.py. A meta deste tutorial é a utilização/realização prática de testes, sem enfatizar tópicos de "engenharia de software" referentes à importância/relevância da utilização de testes, ou mesmo quanto às suas classificações.

Transcript of (2014-11-05) [PythonBrasil] Testando com py.test e tox

Page 1: (2014-11-05) [PythonBrasil] Testando com py.test e tox

Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini2014-11-05 – Recife / PE

Testando com py.test e toxTestando com py.test e tox

Danilo de Jesus da Silva BelliniTwitter: @danilobellini

http://pytest.org/https://tox.readthedocs.org/

https://github.com/schlamar/pytest-covhttp://nedbatchelder.com/code/coverage

Page 2: (2014-11-05) [PythonBrasil] Testando com py.test e tox

Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini2014-11-05 – Recife / PE

Aspectos geraisAspectos gerais

● Breve históriaBreve história– Originalmente, parte do “py”Originalmente, parte do “py”

– Holger Krekel (criador)Holger Krekel (criador)

● Objetivo centralObjetivo central– Automatizar (e padronizar) testesAutomatizar (e padronizar) testes

● Testando...Testando...– CPython 2.x (2.6+) e 3.xCPython 2.x (2.6+) e 3.x

– PyPyPyPy

– Códigos em outras linguagensCódigos em outras linguagens

Page 3: (2014-11-05) [PythonBrasil] Testando com py.test e tox

Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini2014-11-05 – Recife / PE

Ambiente virtual (opcional)Ambiente virtual (opcional)e instalaçãoe instalação

● Admitindo o virtualenv instalado e atualizado (além do pip), crie Admitindo o virtualenv instalado e atualizado (além do pip), crie um diretório “pytut” com o comando:um diretório “pytut” com o comando:

Este será o ambiente para a realização dos testes, além da Este será o ambiente para a realização dos testes, além da instalação do py.test e do tox.instalação do py.test e do tox.

● Ative o ambiente (as linhas ganharão um prefixo):Ative o ambiente (as linhas ganharão um prefixo):

● Instale o py.test, o pytest-cov e o tox (não-opcional):Instale o py.test, o pytest-cov e o tox (não-opcional):

● Para desativar:Para desativar:

$ virtualenv pytut$ virtualenv pytut

$ . ~/pytut/bin/activate$ . ~/pytut/bin/activate

$ deactivate$ deactivate

$ pip install tox pytest pytest-cov$ pip install tox pytest pytest-cov

Ou no diretório de instalação

caso não tenha criado em

$HOME/pytut

Page 4: (2014-11-05) [PythonBrasil] Testando com py.test e tox

Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini2014-11-05 – Recife / PE

Primeiros passosPrimeiros passos

● Arquivo Arquivo test_mul.pytest_mul.py::

● Rodar o teste:Rodar o teste:

● O teste passa?O teste passa?– Corrija o códigoCorrija o código

def multiplica(a, b): return a + b

def test_multiplica_7_8(): assert multiplica(7, 8) == 56

def multiplica(a, b): return a + b

def test_multiplica_7_8(): assert multiplica(7, 8) == 56

$ py.test$ py.test

01_02

01_01

Os números à direita dos slides se referem a uma

sugestão ade organização das etapas do tutorial

em diferentes diretórios ou

commits

Page 5: (2014-11-05) [PythonBrasil] Testando com py.test e tox

Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini2014-11-05 – Recife / PE

Como o py.test encontra os testes?Como o py.test encontra os testes?

● Convenção de nomes!Convenção de nomes!– Prefixos “test_” nos arquivos e funções/métodosPrefixos “test_” nos arquivos e funções/métodos

– Prefixo Test nos nomes das classesPrefixo Test nos nomes das classes

● Tente o exemplo anterior novamente, mas mantendo Tente o exemplo anterior novamente, mas mantendo o arquivo com o nome o arquivo com o nome multiplica.pymultiplica.py– O que acontece?O que acontece?

– E chamando com o nome do arquivo como parâmetro?E chamando com o nome do arquivo como parâmetro?$ py.test multiplica.py$ py.test multiplica.py 01_03

Para organização estrutural em diretórios e outras sugestões/convenções, veja

http://pytest.org/latest/goodpractises.html

Page 6: (2014-11-05) [PythonBrasil] Testando com py.test e tox

Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini2014-11-05 – Recife / PE

FibonacciFibonacci

● Com a função fibonacci em um arquivo Com a função fibonacci em um arquivo fib.pyfib.py ... ...

… … faça rotinas de testes (funções) para pelo menos faça rotinas de testes (funções) para pelo menos 8 diferentes entradas válidas em um arquivo 8 diferentes entradas válidas em um arquivo test_fib.pytest_fib.py

● Rodar com:Rodar com:02_01

def fibonacci(x): return n if n <= 1 else fibonacci(n - 1) + fibonacci(n - 2)def fibonacci(x): return n if n <= 1 else fibonacci(n - 1) + fibonacci(n - 2)

from fib import fibonacci

# Continuar ...

from fib import fibonacci

# Continuar ...

$ py.test$ py.test

Page 7: (2014-11-05) [PythonBrasil] Testando com py.test e tox

Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini2014-11-05 – Recife / PE

Testes parametrizadosTestes parametrizados

● Implemente uma versão alternativa ao Implemente uma versão alternativa ao test_fib.pytest_fib.py utilizando apenas um único teste com vários “assert”utilizando apenas um único teste com vários “assert”– O que acontece com a contagem dos testes?O que acontece com a contagem dos testes?

● Implemente o Implemente o test_fib.pytest_fib.py usando: usando:

02_03

02_02

import pytestfrom fib import fibonacci

schema = "n", "out"table = [ # Pares (entrada "n", saída "out") (0, 0), (1, 1), # ... complete com os demais pares]

@pytest.mark.parametrize(schema, table)def test_mapeia_entrada_saida(n, out): assert fibonacci(n) == out

import pytestfrom fib import fibonacci

schema = "n", "out"table = [ # Pares (entrada "n", saída "out") (0, 0), (1, 1), # ... complete com os demais pares]

@pytest.mark.parametrize(schema, table)def test_mapeia_entrada_saida(n, out): assert fibonacci(n) == out

O que ocorre quando se

utiliza o decorator

pytest.mark.parametrize

mais de uma vez?

Page 8: (2014-11-05) [PythonBrasil] Testando com py.test e tox

Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini2014-11-05 – Recife / PE

Usando mais de uma vez o decorator Usando mais de uma vez o decorator pytest.mark.parametrizepytest.mark.parametrize

Extra!

import pytest

p = pytest.mark.parametrize

@p("a", [0, 1, 2, 3])@p("b", [5, 7, 9, 12])def test_multiplica_parametrizado(a, b): assert multiplica(a, b) == a * b

import pytest

p = pytest.mark.parametrize

@p("a", [0, 1, 2, 3])@p("b", [5, 7, 9, 12])def test_multiplica_parametrizado(a, b): assert multiplica(a, b) == a * b

● Produto cartesiano!Produto cartesiano!● Possibilita mais testes do que linhas de código para Possibilita mais testes do que linhas de código para

estesestes● Insira este “teste” para o “projeto” da multiplicaçãoInsira este “teste” para o “projeto” da multiplicação

– Na prática, testes não são uma mera repetição do códigoNa prática, testes não são uma mera repetição do código

– Útil quando combinado com oráculosÚtil quando combinado com oráculos

01_04

Page 9: (2014-11-05) [PythonBrasil] Testando com py.test e tox

Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini2014-11-05 – Recife / PE

DicasDicas

● Tabelas de dados devem ser iteráveis, não necessariamente Tabelas de dados devem ser iteráveis, não necessariamente listas, e.g.listas, e.g.

● Na presença de muitas rotinas de testes sendo parametrizadas, Na presença de muitas rotinas de testes sendo parametrizadas, pode-se fazer …pode-se fazer …

… … para definir o decorator “@p(...)” para fornecer parâmetros ao para definir o decorator “@p(...)” para fornecer parâmetros ao teste parametrizadoteste parametrizado

● Schema de um único valor não precisa ser uma tuplaSchema de um único valor não precisa ser uma tupla● As mesmas tabelas podem ser utilizadas em diferentes rotinas de As mesmas tabelas podem ser utilizadas em diferentes rotinas de

testes (e.g. testando múltiplas implementações de uma mesma testes (e.g. testando múltiplas implementações de uma mesma tarefa, pattern strategy)tarefa, pattern strategy)

table = enumerate([0, 1, 1, 2, 3, 5, 8, 13])table = enumerate([0, 1, 1, 2, 3, 5, 8, 13]) 02_04

p = pytest.mark.parametrizep = pytest.mark.parametrize

Page 10: (2014-11-05) [PythonBrasil] Testando com py.test e tox

Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini2014-11-05 – Recife / PE

ExceçãoExceção● Modifique a função fibonacci para lançar uma exceção Modifique a função fibonacci para lançar uma exceção

ValueError quando a entrada for negativa.ValueError quando a entrada for negativa.

● Mas como testar?Mas como testar?– Gerenciadores de contexto!Gerenciadores de contexto!

– Crie pelo menos dois testes contendo esse gerenciador de Crie pelo menos dois testes contendo esse gerenciador de contexto, um no qual a exceção deveria ocorrer (entrada contexto, um no qual a exceção deveria ocorrer (entrada negativa) e outro no qual não deveria ocorrer exceção.negativa) e outro no qual não deveria ocorrer exceção.

● Algum teste falhou? Coloque este decorator nele:Algum teste falhou? Coloque este decorator nele:

if n < 0: raise ValueError("Use apenas inteiros positivos")if n < 0: raise ValueError("Use apenas inteiros positivos")

with pytest.raises(ValueError): # Algo que deveria lançar um ValueErrorwith pytest.raises(ValueError): # Algo que deveria lançar um ValueError

[email protected]@pytest.mark.xfail“xfail” = expected to fail

Page 11: (2014-11-05) [PythonBrasil] Testando com py.test e tox

Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini2014-11-05 – Recife / PE

Testando a mensagem de exceçãoTestando a mensagem de exceção● O bloco with apenas testa se a exceção ocorreu. Mas a O bloco with apenas testa se a exceção ocorreu. Mas a

exceção poderia ter ocorrido com uma mensagem diferente exceção poderia ter ocorrido com uma mensagem diferente da desejada (e.g. mais de uma forma para o valor de entrada da desejada (e.g. mais de uma forma para o valor de entrada estar fora do domínio de aplicabilidade da função)estar fora do domínio de aplicabilidade da função)

● Faça um teste usando a entrada “2j” (o número imaginário 2), Faça um teste usando a entrada “2j” (o número imaginário 2), a qual lança um TypeError. Verifique se a mensagem contém a qual lança um TypeError. Verifique se a mensagem contém a palavra “complex”.a palavra “complex”.

02_07

with pytest.raises(TypeError): try: # Bloco que deveria lançar a exceção except TypeError as exc: assert "complex" in str(exc) raise # Propaga o TypeError

with pytest.raises(TypeError): try: # Bloco que deveria lançar a exceção except TypeError as exc: assert "complex" in str(exc) raise # Propaga o TypeError 02_06

with pytest.raises(TypeError) as excinfo: # Bloco que deveria lançar a exceçãoassert "complex" in str(excinfo.value)

with pytest.raises(TypeError) as excinfo: # Bloco que deveria lançar a exceçãoassert "complex" in str(excinfo.value)

… … e usando o py.code.ExceptionInfo() ...e usando o py.code.ExceptionInfo() ...

Dica: Troque o nome “complex” por outro que não existe na

string, a fim de “testar o teste”.

Page 12: (2014-11-05) [PythonBrasil] Testando com py.test e tox

Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini2014-11-05 – Recife / PE

OráculosOráculos● Implementação de referência pronta Implementação de referência pronta

para pelo menos parte do domínio.para pelo menos parte do domínio.● Utilizar uma implementação (completa Utilizar uma implementação (completa

ou parcial) para testar outraou parcial) para testar outra– Possibilidade de criação massiva de testes.Possibilidade de criação massiva de testes.

● Teste esta implementação alternativa Teste esta implementação alternativa para 30 entradas diferentes (pequenas)para 30 entradas diferentes (pequenas)

02_08

phi = .5 + .5 * 5 ** .5 # Golden ratio!def fibonacci_closed_form(n): return int(round(phi ** n * 5 ** -.5))

phi = .5 + .5 * 5 ** .5 # Golden ratio!def fibonacci_closed_form(n): return int(round(phi ** n * 5 ** -.5))

Dica: Use nomes para os testes que permita a seleção com:$ pytest -k parte_do_nome

● Testes aleatóriosTestes aleatórios– Sem oráculos, testes aleatórios Sem oráculos, testes aleatórios

representam a resistência do representam a resistência do código a “falha de segmentação” código a “falha de segmentação” e coisas similares (não faremos e coisas similares (não faremos neste minicurso/tutorial).neste minicurso/tutorial).

– As entradas para uso com um As entradas para uso com um oráculo, dentro dos domínios oráculo, dentro dos domínios relevantes, podem ser geradas relevantes, podem ser geradas aleatoriamente.aleatoriamente.

– ReprodutibilidadeReprodutibilidade● Necessário?Necessário?● Seed fixo ou hard-coded?Seed fixo ou hard-coded?

Dica: Usar o decorator @audiolazy.cached (Python 2/3) ou

o @functools.lru_cache (apenas Python 3) na “fibonacci” original

resolve o problema de desempenho

Page 13: (2014-11-05) [PythonBrasil] Testando com py.test e tox

Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini2014-11-05 – Recife / PE

Apenas coletar testesApenas coletar testes

● Queremos garantir que o oráculo testado com Queremos garantir que o oráculo testado com valores aleatórios tenha sempre o mesmo seed. Mas valores aleatórios tenha sempre o mesmo seed. Mas antes, façamos os testes aleatórios:antes, façamos os testes aleatórios:

● Veja os valores de “n” a cada chamada com:Veja os valores de “n” a cada chamada com:

from random import sample

@p("n", sample(range(32), 16))def test_oraculo_entrada_aleatoria(n): assert fibonacci_closed_form(n) == fibonacci(n)

from random import sample

@p("n", sample(range(32), 16))def test_oraculo_entrada_aleatoria(n): assert fibonacci_closed_form(n) == fibonacci(n)

$ py.test --collect-only$ py.test --collect-only

02_09

Page 14: (2014-11-05) [PythonBrasil] Testando com py.test e tox

Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini2014-11-05 – Recife / PE

Exemplo de conftest.py:Exemplo de conftest.py:Fixando o seedFixando o seed

● Vamos forçar para que os valores de “n” utilizados nos Vamos forçar para que os valores de “n” utilizados nos testes “aleatórios” com oráculo sejam sempre os mesmos.testes “aleatórios” com oráculo sejam sempre os mesmos.

● Crie um arquivo conftest.py no mesmo diretório em que o Crie um arquivo conftest.py no mesmo diretório em que o py.test é chamado contendopy.test é chamado contendo

● pytest_configurepytest_configure– ““Hook” chamado antes da coleta dos testesHook” chamado antes da coleta dos testes

– Há outros “hooks” iniciados com “pytest_” para cada etapa do Há outros “hooks” iniciados com “pytest_” para cada etapa do processo de testesprocesso de testes

import randomdef pytest_configure(config): random.seed(42)

import randomdef pytest_configure(config): random.seed(42) 02_10

Page 15: (2014-11-05) [PythonBrasil] Testando com py.test e tox

Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini2014-11-05 – Recife / PE

Personalização das etapas do processo Personalização das etapas do processo do py.test (“hooks”)do py.test (“hooks”)

http://pytest.org/latest/plugins.html● É possível inserir um processo para antes de cada teste, após É possível inserir um processo para antes de cada teste, após

cada teste, antes da coleta, após a coleta (e.g. reordenar os cada teste, antes da coleta, após a coleta (e.g. reordenar os testes), etc. em um arquivo “conftest.py” com funções testes), etc. em um arquivo “conftest.py” com funções iniciadas em “pytest_”iniciadas em “pytest_”– Normalmente usado em casos específicos, e.g. com warnings para Normalmente usado em casos específicos, e.g. com warnings para

remover as alterações realizadas no próprio globals da remover as alterações realizadas no próprio globals da função/método que realizou o warningfunção/método que realizou o warning

● https://docs.python.org/2/library/warnings.html● Warnings são difíceis de testar, tanto com o fixture padrão “recwarn” do Warnings são difíceis de testar, tanto com o fixture padrão “recwarn” do

py.test como usando diretamente o módulo “warnings” padrão do Python.py.test como usando diretamente o módulo “warnings” padrão do Python.

● Para testar algo nessas condições, é recomendável que se Para testar algo nessas condições, é recomendável que se procure por exemplos antes (e.g. AudioLazy).procure por exemplos antes (e.g. AudioLazy).

Page 16: (2014-11-05) [PythonBrasil] Testando com py.test e tox

Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini2014-11-05 – Recife / PE

Doctests (testes de documentação)Doctests (testes de documentação)● Compara textoCompara texto● Implemente a Implemente a

função ao lado em função ao lado em um arquivo um arquivo nota.pynota.py

● Chame o py.test Chame o py.test com:com:

● Coloque um teste Coloque um teste de documentação de documentação com uma string com uma string como entradacomo entrada

def nome_nota(pitch): """ Nome da nota dado o pitch MIDI (em semitons) caso esteja na pentatônica de Cm.

>>> nome_nota(70) # Bb4 'Bb'

>>> nome_nota(69) # A4 (La central) Traceback (most recent call last): ... ValueError: Fora da escala! """ if pitch % 12 == 0: return "C" if pitch % 12 == 3: return "Eb" if pitch % 12 == 5: return "F" if pitch % 12 == 7: return "G" if pitch % 12 == 10: return "Bb" raise ValueError("Fora da escala!")

def nome_nota(pitch): """ Nome da nota dado o pitch MIDI (em semitons) caso esteja na pentatônica de Cm.

>>> nome_nota(70) # Bb4 'Bb'

>>> nome_nota(69) # A4 (La central) Traceback (most recent call last): ... ValueError: Fora da escala! """ if pitch % 12 == 0: return "C" if pitch % 12 == 3: return "Eb" if pitch % 12 == 5: return "F" if pitch % 12 == 7: return "G" if pitch % 12 == 10: return "Bb" raise ValueError("Fora da escala!") 03_01

$ py.test --doctest-modules$ py.test --doctest-modules

… (Ellipsis):Parte do doctest para “casar com

o que vier”

03_02

Page 17: (2014-11-05) [PythonBrasil] Testando com py.test e tox

Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini2014-11-05 – Recife / PE

Cobertura de códigoCobertura de código● Crie um tox.ini contendoCrie um tox.ini contendo

Assim não é mais necessário digitar parâmetros ao chamar o Assim não é mais necessário digitar parâmetros ao chamar o py.test (exceto parâmetros complementares, e.g. “-k”)py.test (exceto parâmetros complementares, e.g. “-k”)

● --cov-config tox.ini--cov-config tox.ini– Usa o tox.ini como arquivo de configuração do tox, py.test e pytest-cov.Usa o tox.ini como arquivo de configuração do tox, py.test e pytest-cov.

– Avalie a cobertura de código com branching colocando isto também no Avalie a cobertura de código com branching colocando isto também no tox.ini (configura o pytest-cov):tox.ini (configura o pytest-cov):

● --cov nota--cov nota– Especifica o pacote/módulo que deve ser avaliado (nota.py)Especifica o pacote/módulo que deve ser avaliado (nota.py)

● Completar doctests para chegar a 100% de cobertura.Completar doctests para chegar a 100% de cobertura. 03_03

[pytest]addopts = --doctest-modules --cov-config tox.ini --cov-report term-missing --cov nota

[pytest]addopts = --doctest-modules --cov-config tox.ini --cov-report term-missing --cov nota

[run]branch = True[run]branch = True

Tente também com html no lugar de term-missing

Page 18: (2014-11-05) [PythonBrasil] Testando com py.test e tox

Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini2014-11-05 – Recife / PE

ToxTox● Gerencia múltiplos ambientes virtuais, para Gerencia múltiplos ambientes virtuais, para

automatizar os testes em todos os ambientesautomatizar os testes em todos os ambientes● Exigências:Exigências:

– Um arquivo setup.py do projeto (instalação/configuração Um arquivo setup.py do projeto (instalação/configuração do “package”)do “package”)

– Um arquivo tox.ini (configuração do tox e py.test)Um arquivo tox.ini (configuração do tox e py.test)

● Vamos fazer nos 2 primeiros dos 3 “projetos” criados, Vamos fazer nos 2 primeiros dos 3 “projetos” criados, para testar tanto no Python 2 como no Python 3.para testar tanto no Python 2 como no Python 3.

from setuptools import setupsetup(name="pytut")from setuptools import setupsetup(name="pytut") EsteEste setup.py é MÍNIMO.

O nome é usado pelo tox para criar um egg

[tox]envlist = py27, py34

[testenv]deps = pytestcommands = py.test

[tox]envlist = py27, py34

[testenv]deps = pytestcommands = py.test Exemplo de parte do tox.ini

que configura o tox.

$ tox$ tox

01_0502_11

Page 19: (2014-11-05) [PythonBrasil] Testando com py.test e tox

Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini2014-11-05 – Recife / PE

Tox + py.test + doctestsTox + py.test + doctests● Para testar doctests, o py.test avalia todos os “*.py” Para testar doctests, o py.test avalia todos os “*.py”

não apenas os arquivos iniciados com “test_”não apenas os arquivos iniciados com “test_”– Necessário avisar o tox para instalar o pytest-covNecessário avisar o tox para instalar o pytest-cov

– Importar o setup.py gera conflitoImportar o setup.py gera conflito● Soluções possíveis (basta utilizar uma):Soluções possíveis (basta utilizar uma):

● Coloque o tox com o terceiro “projeto” criado neste Coloque o tox com o terceiro “projeto” criado neste tutorialtutorial

[testenv]deps = pytest pytest-covinstall_command = pip install {opts} {packages}

[testenv]deps = pytest pytest-covinstall_command = pip install {opts} {packages}

Altera o comando de instalação das dependências

Extra!

03_04

from setuptools import setupif __name__ == "__main__": setup(name="pytut")

from setuptools import setupif __name__ == "__main__": setup(name="pytut") EvitarEvitar que o “setup”

seja chamado ao importar o setup.py

[pytest]addopts = ... --ignore setup.py

[pytest]addopts = ... --ignore setup.py

Avisar no tox.ini para o py.test ignorar o setup.pypy.test ignorar o setup.py

Page 20: (2014-11-05) [PythonBrasil] Testando com py.test e tox

Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini2014-11-05 – Recife / PE

FixturesFixtures● Já usamos fixtures!Já usamos fixtures!

– pytest.mark.parametrizepytest.mark.parametrize

● São utilizadas como PARÂMETROS nas rotinas de teste. São utilizadas como PARÂMETROS nas rotinas de teste. Podemos, por exemplo, criar uma rotina para acessar um Podemos, por exemplo, criar uma rotina para acessar um recurso que precisa ser liberado ao final de seu uso ou recurso que precisa ser liberado ao final de seu uso ou possui um “mock” (banco de dados, instâcia WSGI, etc.).possui um “mock” (banco de dados, instâcia WSGI, etc.).

● Testaremos algo que acessa dados armazenados em um Testaremos algo que acessa dados armazenados em um arquivo. Começamos avaliando se um arquivo “dados.txt” arquivo. Começamos avaliando se um arquivo “dados.txt” existe e está vazio:existe e está vazio:

# Sem fixture (por enquanto)def test_arquivo_vazio(): with open("dados.txt") as arq: assert not arq.read(1)

# Sem fixture (por enquanto)def test_arquivo_vazio(): with open("dados.txt") as arq: assert not arq.read(1)

04_01

test_arquivo.py

Page 21: (2014-11-05) [PythonBrasil] Testando com py.test e tox

Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini2014-11-05 – Recife / PE

Yield fixtureYield fixture(fixtures personalizadas)(fixtures personalizadas)

● Definida ao estilo de gerenciador de contexto...Definida ao estilo de gerenciador de contexto...

...e nos testes, aparece como parâmetro:...e nos testes, aparece como parâmetro:

● Pode-se usar a mesma fixture em mais de um teste. Pode-se usar a mesma fixture em mais de um teste. Façam isso em um novo teste que verifica se o Façam isso em um novo teste que verifica se o arquivo não está vazio (com xfail).arquivo não está vazio (com xfail).

04_02

04_03

# Sem fixture (por enquanto)def test_arquivo_vazio(arq): assert not arq.read(1)

# Sem fixture (por enquanto)def test_arquivo_vazio(arq): assert not arq.read(1)

@pytest.yield_fixturedef arq(): with open("dados.txt") as f: yield f

@pytest.yield_fixturedef arq(): with open("dados.txt") as f: yield f

@pytest.yield_fixturedef nome_fixture(): # setup yield valor # teardown

@pytest.yield_fixturedef nome_fixture(): # setup yield valor # teardown Modelo

Page 22: (2014-11-05) [PythonBrasil] Testando com py.test e tox

Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini2014-11-05 – Recife / PE

EscopoEscopo● É possível fazer os dois testes (testar se vazio e testar se não vazio) É possível fazer os dois testes (testar se vazio e testar se não vazio)

passarem ao mesmo tempo?passarem ao mesmo tempo?

...e se o arquivo mudar entre os testes?...e se o arquivo mudar entre os testes?

@pytest.yield_fixturedef arq(): with open("dados.txt", "r+") as f: backup = f.read() yield f with open("dados.txt", "w") as f: f.write(backup)

def test_arquivo_vazio(arq): arq.seek(0) assert not arq.read(1) arq.write("A-ha!")

def test_arquivo_nao_vazio(arq): arq.seek(0) assert arq.read(1)

@pytest.yield_fixturedef arq(): with open("dados.txt", "r+") as f: backup = f.read() yield f with open("dados.txt", "w") as f: f.write(backup)

def test_arquivo_vazio(arq): arq.seek(0) assert not arq.read(1) arq.write("A-ha!")

def test_arquivo_nao_vazio(arq): arq.seek(0) assert arq.read(1)

EVITAR! Normalmente testes são INdependentes entre si

@pytest.yield_fixture(scope="session")@pytest.yield_fixture(scope="session")

04_04

Funciona? E se a fixture obtiver “f” somente uma vez na sessão?Funciona? E se a fixture obtiver “f” somente uma vez na sessão?

04_05

O uso de escopo permite otimizar testes evitando a necessidade de setup/teardown para cada teste

Page 23: (2014-11-05) [PythonBrasil] Testando com py.test e tox

Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini2014-11-05 – Recife / PE

Outros recursos do py.test:Outros recursos do py.test:Arquivos temporáriosArquivos temporários

● Há diversas formas usando a standard libraryHá diversas formas usando a standard library

● Ou usando o plugin “tmpdir” do py.testOu usando o plugin “tmpdir” do py.test– http://pytest.org/latest/tmpdir.html

from tempfile import NamedTemporaryFileimport pytest

@pytest.yield_fixturedef tmp(): with NamedTemporaryFile() as f: yield f # f.name possui o nome do arquivo

from tempfile import NamedTemporaryFileimport pytest

@pytest.yield_fixturedef tmp(): with NamedTemporaryFile() as f: yield f # f.name possui o nome do arquivo

Page 24: (2014-11-05) [PythonBrasil] Testando com py.test e tox

Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini2014-11-05 – Recife / PE

Skip/xfailSkip/xfail

● Há testes que podem fazer sentido em somente um dos Há testes que podem fazer sentido em somente um dos ambientes (e.g. testes que dependem do ambientes (e.g. testes que dependem do itertools.accumulate ou do functools.lru_cache que itertools.accumulate ou do functools.lru_cache que existem apenas no Python 3).existem apenas no Python 3).

● Decorators (“mark”), [possivelmente] com condições para Decorators (“mark”), [possivelmente] com condições para realização do(s) teste(s) marcadosrealização do(s) teste(s) marcados– pytest.mark.skipifpytest.mark.skipif

– pytest.mark.xfailpytest.mark.xfail

● Imperativo (comandos)Imperativo (comandos)– pytest.skippytest.skip

– pytest.xfailpytest.xfail

Usamos o mark.xfail

incondicional no 02_05

Page 25: (2014-11-05) [PythonBrasil] Testando com py.test e tox

Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini2014-11-05 – Recife / PE

Mock/stub/fake/dummyMock/stub/fake/dummycom a fixture monkeypatchcom a fixture monkeypatch

● Fixture monkeypatchFixture monkeypatch– Método “setattr”Método “setattr”

– Mesma sintaxe do built-in setattr (e __setattr__)Mesma sintaxe do built-in setattr (e __setattr__)

– Monkeypatch.setattr()Monkeypatch.setattr()

● http://pytest.org/latest/monkeypatch.html

Page 26: (2014-11-05) [PythonBrasil] Testando com py.test e tox

Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini2014-11-05 – Recife / PE

Ponto flutuantePonto flutuante

● Comparar por aproximaçãoComparar por aproximação– Valor absoluto da diferençaValor absoluto da diferença

– Erro percentual/relativo (não-simétrico)Erro percentual/relativo (não-simétrico)

– Tolerância em número de bits de mantissaTolerância em número de bits de mantissa

● Há implementações prontasHá implementações prontas– numpy.isclose, numpy.allclosenumpy.isclose, numpy.allclose

– audiolazy.almost_eqaudiolazy.almost_eq

– unittest.TestCase.assertAlmostEqualunittest.TestCase.assertAlmostEqual

● Uso de arredondamentos explícitosUso de arredondamentos explícitos

É possível usar o oráculo do

Fibonacci para verificar os dígitos mais significativos de valores maiores

de entrada?

Extra!

Page 27: (2014-11-05) [PythonBrasil] Testando com py.test e tox

Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini2014-11-05 – Recife / PE

MiscelâneaMiscelânea

● Vejam a ajuda do py.test!Vejam a ajuda do py.test!– Todos os parâmetros são opcionais, mas quais são todos Todos os parâmetros são opcionais, mas quais são todos

os parâmetros?os parâmetros?

● Há muita documentação na InternetHá muita documentação na Internet– Links na apresentação (primeiro e último slides)Links na apresentação (primeiro e último slides)

● Open source!Open source!– Colaborações =)Colaborações =)

$ py.test --helpusage: py.test [options] [file_or_dir] [file_or_dir] [...]...

$ py.test --helpusage: py.test [options] [file_or_dir] [file_or_dir] [...]...

Page 28: (2014-11-05) [PythonBrasil] Testando com py.test e tox

Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini2014-11-05 – Recife / PE

Perguntas

Perguntas

?FIM!FIM!

Obrigado!Obrigado!

http://pytest.org/http://pytest.org/httphttps://tox.readthedocs.org/s://tox.readthedocs.org/

https://github.chttps://github.com/schlamar/pytest-covom/schlamar/pytest-covhttp://nedbatchelhttp://nedbatchelder.com/code/coverageder.com/code/coverage