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.
(2014-11-05) [PythonBrasil] Testando com py.test e tox
1. Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini
2014-11-05 – Recife / PE
TTeessttaannddoo ccoomm ppyy..tteesstt ee ttooxx
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
2. Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini
2014-11-05 – Recife / PE
AAssppeeccttooss ggeerraaiiss
● BBrreevvee hhiissttóórriiaa
– OOrriiggiinnaallmmeennttee,, ppaarrttee ddoo ““ppyy””
– HHoollggeerr KKrreekkeell ((ccrriiaaddoorr))
● OObbjjeettiivvoo cceennttrraall
– AAuuttoommaattiizzaarr ((ee ppaaddrroonniizzaarr)) tteesstteess
● TTeessttaannddoo......
– CCPPyytthhoonn 22..xx ((22..66++)) ee 33..xx
– PPyyPPyy
– CCóóddiiggooss eemm oouuttrraass lliinngguuaaggeennss
3. Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini
2014-11-05 – Recife / PE
AAmmbbiieennttee vviirrttuuaall ((ooppcciioonnaall))
ee iinnssttaallaaççããoo
● AAddmmiittiinnddoo oo vviirrttuuaalleennvv iinnssttaallaaddoo ee aattuuaalliizzaaddoo ((aalléémm ddoo ppiipp)),, ccrriiee
uumm ddiirreettóórriioo ““ppyyttuutt”” ccoomm oo ccoommaannddoo::
EEssttee sseerráá oo aammbbiieennttee ppaarraa aa rreeaalliizzaaççããoo ddooss tteesstteess,, aalléémm ddaa
iinnssttaallaaççããoo ddoo ppyy..tteesstt ee ddoo ttooxx..
● AAttiivvee oo aammbbiieennttee ((aass lliinnhhaass ggaannhhaarrããoo uumm pprreeffiixxoo))::
● IInnssttaallee oo ppyy..tteesstt,, oo ppyytteesstt--ccoovv ee oo ttooxx ((nnããoo--ooppcciioonnaall))::
● PPaarraa ddeessaattiivvaarr::
$$ vviirrttuuaalleennvv ppyyttuutt
$$ .. ~~//ppyyttuutt//bbiinn//aaccttiivvaattee
$$ ppiipp iinnssttaallll ttooxx ppyytteesstt ppyytteesstt--ccoovv
$$ ddeeaaccttiivvaattee
Ou no diretório
de instalação
caso não tenha
criado em
$HOME/pytut
4. Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini
2014-11-05 – Recife / PE
PPrriimmeeiirrooss ppaassssooss
● AArrqquuiivvoo tteesstt__mmuull..ppyy::
● RRooddaarr oo tteessttee::
def multiplica(a, b):
def multiplica(a, b):
def test_multiplica_7_8():
● OO tteessttee ppaassssaa??
– CCoorrrriijjaa oo ccóóddiiggoo
return a + b
return a + b
def test_multiplica_7_8():
assert multiplica(7, 8) == 56
assert multiplica(7, 8) == 56
$$ ppyy..tteesstt
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
01_01
01_02
5. Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini
2014-11-05 – Recife / PE
CCoommoo oo ppyy..tteesstt eennccoonnttrraa ooss tteesstteess??
● CCoonnvveennççããoo ddee nnoommeess!!
– PPrreeffiixxooss ““tteesstt__”” nnooss aarrqquuiivvooss ee ffuunnççõõeess//mmééttooddooss
– PPrreeffiixxoo TTeesstt nnooss nnoommeess ddaass ccllaasssseess
● TTeennttee oo eexxeemmpplloo aanntteerriioorr nnoovvaammeennttee,, mmaass mmaanntteennddoo
oo aarrqquuiivvoo ccoomm oo nnoommee mmuullttiipplliiccaa..ppyy
– OO qquuee aaccoonntteeccee??
– EE cchhaammaannddoo ccoomm oo nnoommee ddoo aarrqquuiivvoo ccoommoo ppaarrââmmeettrroo??
$$ ppyy..tteesstt mmuullttiipplliiccaa..ppyy 01_03
Para organização estrutural em diretórios e outras
sugestões/convenções, veja
http://pytest.org/latest/goodpractises.html
6. Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini
2014-11-05 – Recife / PE
FFiibboonnaaccccii
● CCoomm aa ffuunnççããoo ffiibboonnaaccccii eemm uumm aarrqquuiivvoo ffiibb..ppyy ......
def fibonacci(x):
def fibonacci(x):
return n if n <= 1 else fibonacci(n - 1) + fibonacci(n - 2)
return n if n <= 1 else fibonacci(n - 1) + fibonacci(n - 2)
…… ffaaççaa rroottiinnaass ddee tteesstteess ((ffuunnççõõeess)) ppaarraa ppeelloo mmeennooss
88 ddiiffeerreenntteess eennttrraaddaass vváálliiddaass eemm uumm aarrqquuiivvoo
tteesstt__ffiibb..ppyy
from fib import fibonacci
from fib import fibonacci
# Continuar ...
# Continuar ...
● RRooddaarr ccoomm:: $$ ppyy..tteesstt
02_01
7. Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini
2014-11-05 – Recife / PE
TTeesstteess ppaarraammeettrriizzaaddooss
● IImmpplleemmeennttee uummaa vveerrssããoo aalltteerrnnaattiivvaa aaoo tteesstt__ffiibb..ppyy
uuttiilliizzaannddoo aappeennaass uumm úúnniiccoo tteessttee ccoomm vváárriiooss ““aasssseerrtt””
– OO qquuee aaccoonntteeccee ccoomm aa ccoonnttaaggeemm ddooss tteesstteess??
● IImmpplleemmeennttee oo tteesstt__ffiibb..ppyy uussaannddoo::
02_02
02_03
import pytest
from fib import fibonacci
import pytest
from fib import fibonacci
schema = "n", "out"
table = [ # Pares (entrada "n", saída "out")
schema = "n", "out"
table = [ # Pares (entrada "n", saída "out")
(0, 0),
(1, 1),
# ... complete com os demais pares
(0, 0),
(1, 1),
# ... complete com os demais pares
]
]
@pytest.mark.parametrize(schema, table)
def test_mapeia_entrada_saida(n, out):
@pytest.mark.parametrize(schema, table)
def test_mapeia_entrada_saida(n, out):
assert fibonacci(n) == out
assert fibonacci(n) == out
O que ocorre
quando se
utiliza o
decorator
pytest.mark.p
arametrize
mais de uma
vez?
8. Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini
2014-11-05 – Recife / PE
UUssaannddoo mmaaiiss ddee uummaa vveezz oo ddeeccoorraattoorr
ppyytteesstt..mmaarrkk..ppaarraammeettrriizzee
Extra!
● PPrroodduuttoo ccaarrtteessiiaannoo!!
● PPoossssiibbiilliittaa mmaaiiss tteesstteess ddoo qquuee lliinnhhaass ddee ccóóddiiggoo ppaarraa
eesstteess
● IInnssiirraa eessttee ““tteessttee”” ppaarraa oo ““pprroojjeettoo”” ddaa mmuullttiipplliiccaaççããoo
– NNaa pprrááttiiccaa,, tteesstteess nnããoo ssããoo uummaa mmeerraa rreeppeettiiççããoo ddoo ccóóddiiggoo
– ÚÚttiill qquuaannddoo ccoommbbiinnaaddoo ccoomm oorrááccuullooss
import pytest
import pytest
p = pytest.mark.parametrize
@p("a", [0, 1, 2, 3])
@p("b", [5, 7, 9, 12])
def test_multiplica_parametrizado(a, b):
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
assert multiplica(a, b) == a * b
01_04
10. Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini
2014-11-05 – Recife / PE
EExxcceeççããoo
● MMooddiiffiiqquuee aa ffuunnççããoo ffiibboonnaaccccii ppaarraa llaannççaarr uummaa eexxcceeççããoo
VVaalluueeEErrrroorr qquuaannddoo aa eennttrraaddaa ffoorr nneeggaattiivvaa..
if n < 0:
if n < 0:
raise ValueError("Use apenas inteiros positivos")
raise ValueError("Use apenas inteiros positivos")
● MMaass ccoommoo tteessttaarr??
– GGeerreenncciiaaddoorreess ddee ccoonntteexxttoo!!
with pytest.raises(ValueError):
with pytest.raises(ValueError):
# Algo que deveria lançar um ValueError
# Algo que deveria lançar um ValueError
– CCrriiee ppeelloo mmeennooss ddooiiss tteesstteess ccoonntteennddoo eessssee ggeerreenncciiaaddoorr ddee
ccoonntteexxttoo,, uumm nnoo qquuaall aa eexxcceeççããoo ddeevveerriiaa ooccoorrrreerr ((eennttrraaddaa
nneeggaattiivvaa)) ee oouuttrroo nnoo qquuaall nnããoo ddeevveerriiaa ooccoorrrreerr eexxcceeççããoo..
● AAllgguumm tteessttee ffaallhhoouu?? CCoollooqquuee eessttee ddeeccoorraattoorr nneellee::
“xfail” = expected to fail @@ppyytteesstt..mmaarrkk..xxffaaiill 02_05
11. Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini
2014-11-05 – Recife / PE
TTeessttaannddoo aa mmeennssaaggeemm ddee eexxcceeççããoo
● OO bbllooccoo wwiitthh aappeennaass tteessttaa ssee aa eexxcceeççããoo ooccoorrrreeuu.. MMaass aa
eexxcceeççããoo ppooddeerriiaa tteerr ooccoorrrriiddoo ccoomm uummaa mmeennssaaggeemm ddiiffeerreennttee
ddaa ddeesseejjaaddaa ((ee..gg.. mmaaiiss ddee uummaa ffoorrmmaa ppaarraa oo vvaalloorr ddee eennttrraaddaa
eessttaarr ffoorraa ddoo ddoommíínniioo ddee aapplliiccaabbiilliiddaaddee ddaa ffuunnççããoo))
● FFaaççaa uumm tteessttee uussaannddoo aa eennttrraaddaa ““22jj”” ((oo nnúúmmeerroo iimmaaggiinnáárriioo 22)),,
aa qquuaall llaannççaa uumm TTyyppeeEErrrroorr.. VVeerriiffiiqquuee ssee aa mmeennssaaggeemm ccoonnttéémm
aa ppaallaavvrraa ““ccoommpplleexx””..
02_07
with pytest.raises(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
try:
# Bloco que deveria lançar a exceção
except TypeError as exc:
assert "complex" in str(exc)
raise # Propaga o TypeError 02_06
…… ee uussaannddoo oo ppyy..ccooddee..EExxcceeppttiioonnIInnffoo(()) ......
with pytest.raises(TypeError) as excinfo:
with pytest.raises(TypeError) as excinfo:
# Bloco que deveria lançar a exceção
# Bloco que deveria lançar a exceção
assert "complex" in str(excinfo.value)
assert "complex" in str(excinfo.value)
Dica: Troque o nome
“complex” por outro
que não existe na
string, a fim de “testar
o teste”.
12. Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini
2014-11-05 – Recife / PE
OOrrááccuullooss
● IImmpplleemmeennttaaççããoo ddee rreeffeerrêênncciiaa pprroonnttaa
ppaarraa ppeelloo mmeennooss ppaarrttee ddoo ddoommíínniioo..
● UUttiilliizzaarr uummaa iimmpplleemmeennttaaççããoo ((ccoommpplleettaa
oouu ppaarrcciiaall)) ppaarraa tteessttaarr oouuttrraa
– PPoossssiibbiilliiddaaddee ddee ccrriiaaççããoo mmaassssiivvaa ddee tteesstteess..
● TTeessttee eessttaa iimmpplleemmeennttaaççããoo aalltteerrnnaattiivvaa
ppaarraa 3300 eennttrraaddaass ddiiffeerreenntteess ((ppeeqquueennaass))
– SSeemm oorrááccuullooss,, tteesstteess aalleeaattóórriiooss
rreepprreesseennttaamm aa rreessiissttêênncciiaa ddoo
ccóóddiiggoo aa ““ffaallhhaa ddee sseeggmmeennttaaççããoo””
ee ccooiissaass ssiimmiillaarreess ((nnããoo ffaarreemmooss
nneessttee mmiinniiccuurrssoo//ttuuttoorriiaall))..
– AAss eennttrraaddaass ppaarraa uussoo ccoomm uumm
oorrááccuulloo,, ddeennttrroo ddooss ddoommíínniiooss
rreelleevvaanntteess,, ppooddeemm sseerr ggeerraaddaass
aalleeaattoorriiaammeennttee..
– RReepprroodduuttiibbiilliiddaaddee
02_08
phi = .5 + .5 * 5 ** .5 # Golden ratio!
def fibonacci_closed_form(n):
phi = .5 + .5 * 5 ** .5 # Golden ratio!
def fibonacci_closed_form(n):
● TTeesstteess aalleeaattóórriiooss
return int(round(phi ** n * 5 ** -.5))
return int(round(phi ** n * 5 ** -.5))
● NNeecceessssáárriioo??
● SSeeeedd ffiixxoo oouu hhaarrdd--ccooddeedd??
Dica: Use nomes para os testes que permita a seleção com:
$ pytest -k parte_do_nome
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
13. Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini
2014-11-05 – Recife / PE
AAppeennaass ccoolleettaarr tteesstteess
● QQuueerreemmooss ggaarraannttiirr qquuee oo oorrááccuulloo tteessttaaddoo ccoomm
vvaalloorreess aalleeaattóórriiooss tteennhhaa sseemmpprree oo mmeessmmoo sseeeedd.. MMaass
aanntteess,, ffaaççaammooss ooss tteesstteess aalleeaattóórriiooss::
from random import sample
@p("n", sample(range(32), 16))
def test_oraculo_entrada_aleatoria(n):
from random import sample
@p("n", sample(range(32), 16))
def test_oraculo_entrada_aleatoria(n):
assert fibonacci_closed_form(n) == fibonacci(n)
assert fibonacci_closed_form(n) == fibonacci(n)
● VVeejjaa ooss vvaalloorreess ddee ““nn”” aa ccaaddaa cchhaammaaddaa ccoomm::
$$ ppyy..tteesstt ----ccoolllleecctt--oonnllyy
02_09
16. Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini
2014-11-05 – Recife / PE
DDoocctteessttss (tteesstteess ddee ddooccuummeennttaaççããoo))
● CCoommppaarraa tteexxttoo
● IImmpplleemmeennttee aa
ffuunnççããoo aaoo llaaddoo eemm
uumm aarrqquuiivvoo nnoottaa..ppyy
● CChhaammee oo ppyy..tteesstt
ccoomm::
● CCoollooqquuee uumm tteessttee
ddee ddooccuummeennttaaççããoo
ccoomm uummaa ssttrriinngg
ccoommoo eennttrraaddaa
def nome_nota(pitch):
def nome_nota(pitch):
"""
"""
Nome Nome da da nota nota dado dado o o pitch pitch MIDI
MIDI
((em em semitons) semitons) caso caso esteja esteja na
na
pentatônica pentatônica de de Cm.
Cm.
>>> >>> nome_nome_nota(nota(70) 70) # # Bb4
Bb4
''Bb'
Bb'
>>> nome_nota(69) # A4 (La central)
Traceback (most recent call last):
>>> nome_nota(69) # A4 (La central)
Traceback (most recent call last):
...
...
ValueError: Fora da escala!
"""
if pitch % 12 == 0:
ValueError: Fora da escala!
"""
if pitch % 12 == 0:
return "C"
return "C"
if pitch % 12 == 3:
if pitch % 12 == 3:
return "Eb"
return "Eb"
if pitch % 12 == 5:
if pitch % 12 == 5:
return "F"
return "F"
if pitch % 12 == 7:
if pitch % 12 == 7:
return "G"
return "G"
if pitch % 12 == 10:
if pitch % 12 == 10:
return "Bb"
return "Bb"
raise raise ValueError("ValueError("Fora Fora da da escala!") escala!")
03_01
$$ ppyy..tteesstt ----ddoocctteesstt--mmoodduulleess
… (Ellipsis):
Parte do doctest
para “casar com
o que vier”
03_02
17. Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini
2014-11-05 – Recife / PE
CCoobbeerrttuurraa ddee ccóóddiiggoo
● CCrriiee uumm ttooxx..iinnii ccoonntteennddoo
[pytest]
addopts = --doctest-modules
[pytest]
addopts = --doctest-modules
Tente também com html
no lugar de term-missing
--cov-config tox.ini
--cov-report term-missing
--cov nota
--cov-config tox.ini
--cov-report term-missing
--cov nota
AAssssiimm nnããoo éé mmaaiiss nneecceessssáárriioo ddiiggiittaarr ppaarrââmmeettrrooss aaoo cchhaammaarr oo
ppyy..tteesstt (eexxcceettoo ppaarrââmmeettrrooss ccoommpplleemmeennttaarreess,, ee..gg.. ““--kk””))
● ----ccoovv--ccoonnffiigg ttooxx..iinnii
– UUssaa oo ttooxx..iinnii ccoommoo aarrqquuiivvoo ddee ccoonnffiigguurraaççããoo ddoo ttooxx,, ppyy..tteesstt ee ppyytteesstt--ccoovv..
– AAvvaalliiee aa ccoobbeerrttuurraa ddee ccóóddiiggoo ccoomm bbrraanncchhiinngg ccoollooccaannddoo iissttoo ttaammbbéémm nnoo
ttooxx..iinnii (ccoonnffiigguurraa oo ppyytteesstt--ccoovv))::
● ----ccoovv nnoottaa
[run]
branch = True
[run]
branch = True
– EEssppeecciiffiiccaa oo ppaaccoottee//mmóódduulloo qquuee ddeevvee sseerr aavvaalliiaaddoo (nnoottaa..ppyy))
● CCoommpplleettaarr ddoocctteessttss ppaarraa cchheeggaarr aa 110000%% ddee ccoobbeerrttuurraa.. 03_03
18. Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini
2014-11-05 – Recife / PE
TTooxx
● Geerreenncciiaa mmúúllttiippllooss aammbbiieenntteess vviirrttuuaaiiss,, ppaarraa
aauuttoommaattiizzaarr ooss tteesstteess eemm ttooddooss ooss aammbbiieenntteess
● EExxiiggêênncciiaass::
– UUmm aarrqquuiivvoo sseettuupp..ppyy ddoo pprroojjeettoo (iinnssttaallaaççããoo//ccoonnffiigguurraaççããoo
ddoo ““ppaacckkaaggee””))
– UUmm aarrqquuiivvoo ttooxx..iinnii (ccoonnffiigguurraaççããoo ddoo ttooxx ee ppyy..tteesstt))
● VVaammooss ffaazzeerr nnooss 22 pprriimmeeiirrooss ddooss 33 ““pprroojjeettooss”” ccrriiaaddooss,,
ppaarraa tteessttaarr ttaannttoo nnoo PPyytthhoonn 22 ccoommoo nnoo PPyytthhoonn 33..
from from setuptools setuptools import import setup
setup
setup(setup(name="name="pytut") pytut")
EEssttee setup.py é MÍNIMO.
O nome é usado pelo tox para
criar um egg
[tox]
envlist = py27, py34
[tox]
envlist = py27, py34
[testenv]
deps = pytest
commands = py.test
[testenv]
deps = pytest
commands = py.test Exemplo de parte do tox.ini
que configura o tox.
01_05
02_11
$$ ttooxx
19. Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini
2014-11-05 – Recife / PE
TTooxx ++ ppyy..tteesstt ++ ddoocctteessttss
Extra!
● PPaarraa tteessttaarr ddoocctteessttss,, oo ppyy..tteesstt aavvaalliiaa ttooddooss ooss ““**..ppyy””
nnããoo aappeennaass ooss aarrqquuiivvooss iinniicciiaaddooss ccoomm ““tteesstt__””
– NNeecceessssáárriioo aavviissaarr oo ttooxx ppaarraa iinnssttaallaarr oo ppyytteesstt--ccoovv
[testenv]
deps = pytest
[testenv]
deps = pytest
pytest-cov
pytest-cov
Altera o comando de
instalação das dependências
install_command = pip install {opts} {packages}
install_command = pip install {opts} {packages}
– IImmppoorrttaarr oo sseettuupp..ppyy ggeerraa ccoonnfflliittoo
● SSoolluuççõõeess ppoossssíívveeiiss (bbaassttaa uuttiilliizzaarr uummaa))::
from setuptools import setup
if __name__ == "__main__":
setup(name="pytut") EEvviittaarr que o “setup”
Avisar no tox.ini para o
ppyy..tteesstt iiggnnoorraarr oo sseettuupp..ppyy
[pytest]
addopts = ...
--ignore setup.py
● CCoollooqquuee oo ttooxx ccoomm oo tteerrcceeiirroo ““pprroojjeettoo”” ccrriiaaddoo nneessttee
ttuuttoorriiaall
03_04
from setuptools import setup
if __name__ == "__main__":
setup(name="pytut")
seja chamado ao
importar o setup.py
[pytest]
addopts = ...
--ignore setup.py
20. Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini
2014-11-05 – Recife / PE
FFiixxttuurreess
● JJáá uussaammooss ffiixxttuurreess!!
– ppyytteesstt..mmaarrkk..ppaarraammeettrriizzee
● SSããoo uuttiilliizzaaddaass ccoommoo PPAARRÂÂMMEETTRROOSS nnaass rroottiinnaass ddee tteessttee..
PPooddeemmooss,, ppoorr eexxeemmpplloo,, ccrriiaarr uummaa rroottiinnaa ppaarraa aacceessssaarr uumm
rreeccuurrssoo qquuee pprreecciissaa sseerr lliibbeerraaddoo aaoo ffiinnaall ddee sseeuu uussoo oouu
ppoossssuuii uumm ““mmoocckk”” (bbaannccoo ddee ddaaddooss,, iinnssttââcciiaa WWSSGII,, eettcc..))..
● TTeessttaarreemmooss aallggoo qquuee aacceessssaa ddaaddooss aarrmmaazzeennaaddooss eemm uumm
aarrqquuiivvoo.. CCoommeeççaammooss aavvaalliiaannddoo ssee uumm aarrqquuiivvoo ““ddaaddooss..ttxxtt””
eexxiissttee ee eessttáá vvaazziioo::
# Sem fixture (por enquanto)
def test_arquivo_vazio():
# Sem fixture (por enquanto)
def test_arquivo_vazio():
with open("dados.txt") as arq:
with open("dados.txt") as arq:
assert not arq.read(1)
assert not arq.read(1)
04_01
test_arquivo.py
21. Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini
2014-11-05 – Recife / PE
YYiieelldd ffiixxttuurree
(ffiixxttuurreess ppeerrssoonnaalliizzaaddaass))
@pytest.yield_fixture
def nome_fixture():
# setup
yield valor
# teardown Modelo
● DDeeffiinniiddaa aaoo eessttiilloo ddee ggeerreenncciiaaddoorr ddee ccoonntteexxttoo......
@pytest.yield_fixture
def arq():
@pytest.yield_fixture
def arq():
with open("dados.txt") as f:
with open("dados.txt") as f:
yield f
yield f
......ee nnooss tteesstteess,, aappaarreeccee ccoommoo ppaarrââmmeettrroo::
04_02
# Sem fixture (por enquanto)
def test_arquivo_vazio(arq):
assert not arq.read(1)
● PPooddee--ssee uussaarr aa mmeessmmaa ffiixxttuurree eemm mmaaiiss ddee uumm tteessttee..
FFaaççaamm iissssoo eemm uumm nnoovvoo tteessttee qquuee vveerriiffiiccaa ssee oo
aarrqquuiivvoo nnããoo eessttáá vvaazziioo (ccoomm xxffaaiill))..
04_03
# Sem fixture (por enquanto)
def test_arquivo_vazio(arq):
assert not arq.read(1)
@pytest.yield_fixture
def nome_fixture():
# setup
yield valor
# teardown
22. Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini
2014-11-05 – Recife / PE
EEssccooppoo
● ÉÉ ppoossssíívveell ffaazzeerr ooss ddooiiss tteesstteess ((tteessttaarr ssee vvaazziioo ee tteessttaarr ssee nnããoo vvaazziioo))
ppaassssaarreemm aaoo mmeessmmoo tteemmppoo??
......ee ssee oo aarrqquuiivvoo mmuuddaarr eennttrree ooss tteesstteess??
@pytest.yield_fixture
def arq():
@pytest.yield_fixture
def arq():
with open("dados.txt", "r+") as f:
with open("dados.txt", "r+") as f:
backup = f.read()
yield f
backup = f.read()
yield f
with open("dados.txt", "w") as f:
with open("dados.txt", "w") as f:
f.write(backup)
f.write(backup)
def test_arquivo_vazio(arq):
def test_arquivo_vazio(arq):
arq.seek(0)
assert not arq.read(1)
arq.write("A-ha!")
arq.seek(0)
assert not arq.read(1)
arq.write("A-ha!")
def test_arquivo_nao_vazio(arq):
def test_arquivo_nao_vazio(arq):
arq.seek(0)
assert arq.read(1)
arq.seek(0)
assert arq.read(1)
EVITAR! Normalmente
testes são
INdependentes entre si
@@ppyytteesstt..yyiieelldd__ffiixxttuurree((ssccooppee==""sseessssiioonn""))
04_04
FFuunncciioonnaa?? EE ssee aa ffiixxttuurree oobbttiivveerr ““ff”” ssoommeennttee uummaa vveezz nnaa sseessssããoo??
04_05
O uso de escopo
permite otimizar
testes evitando a
necessidade de
setup/teardown
para cada teste
23. Testando com py.test e tox – Danilo J. S. Bellini – @danilobellini
2014-11-05 – Recife / PE
OOuuttrrooss rreeccuurrssooss ddoo ppyy..tteesstt::
AArrqquuiivvooss tteemmppoorráárriiooss
● HHáá ddiivveerrssaass ffoorrmmaass uussaannddoo aa ssttaannddaarrdd lliibbrraarryy
from tempfile import NamedTemporaryFile
import pytest
@pytest.yield_fixture
def tmp():
from tempfile import NamedTemporaryFile
import pytest
@pytest.yield_fixture
def tmp():
with NamedTemporaryFile() as f:
with NamedTemporaryFile() as f:
yield f # f.name possui o nome do arquivo
yield f # f.name possui o nome do arquivo
● OOuu uussaannddoo oo pplluuggiinn ““ttmmppddiirr”” ddoo ppyy..tteesstt
– http://pytest.org/latest/tmpdir.html