simple is better than complex
The Zen of Python (import this)
Qualidade levada a sério em Python
• Master Coaching Trainer
• Pesquisador de segurança sênior
• Programador C/C++ e Python
• Data Scientist
• Viciado em competições de ML
• Coordenador equipe ameaças PSafe
5° software mais baixado
Maior empresa mobile LA
200 mil downloads/dia
Pessoas
Processo
Produto
Versionamento
Qualidade
Automatização
Requisitos
Metodologia
Processo = Engenharia de software
Testes
Analise estática
Bug Tracking
Documentação
Testes de
software
Específicos
Auto descritivos
Auto suficientes
Indicar o comportamento esperado
Servir de documentação
Bugs tendem a virar um caso de teste
Unit Tests
PyUnit
Unit Tests
assertTrue
assertEqual
assertAlmostEqual
assertIn
Mão na massa!
Test Fixture
Test Case
Test Suite
Test Runner
Unit Tests
Test Fixture
Test Fixture
class SimpleTestCase(unittest.TestCase):
def setUp(self):
"""Call before every test case.""“
self.foo = Foo()
self.file = open( "blah", "r" )
def tearDown(self):
"""Call after every test case.""“
self.file.close()
OQueTaSendoTestando_Parametros_RetornoEsperado
get_client_name__with_invalid__params_must_return_False
Test Case
def test_get_client_name__with_invalid__params_must_return_False (self):
"""Test case 1. note that all test method names must begin with 'test.'""“
self.assertEqual(foo.get_client_name(None) , False)
OQueTaSendoTestando_Parametros_RetornoEsperado
get_client_name__with_invalid__params_must_return_False
import unittest
from foobarbaz import Foo # code from module you're testing
class SimpleTestCase(unittest.TestCase):
def setUp(self):
"""Call before every test case.""“
self.foo = Foo()
self.file = open( "blah", "r" )
def tearDown(self):
"""Call after every test case.""“
self.file.close()
def test_get_client_name__with_invalid__params_must_return_False (self):
"""Test case 1. note that all test method names must begin with 'test.'""“
self.assertEqual(foo.get_client_name(None) , False)
Test Suite
Gerencia os testes e fornece uma interface de acesso.
Test Runner
Teste se torna o primeiro cliente do seu código
Somente o codigo necessario é criado
Erros são identificados mais rapidamente
Ganho de produtividade
Códigos entregues realmente prontos
Facilita depuração
Integration Tests
Integration Tests
def test_add_source_must_check_duplicated(self):
psw = PhishingServiceWrapper()
psw.add_url('http://www.testededominio.com.br', source_id=1)
add_ret = psw.add_url('http://www.testededominio.com.br', source_id=1)
self.assertEqual(add_ret['status'], 'duplicated')
Performance Tests
Performance Tests
__main__.SomeTest.testOne: 1.001
__main__.SomeTest.testTwo: 2.002
-----------------------------------------------
Ran 2 tests in 3.003s OK
Performance Tests
import cProfile
import unittest
if __name__ == '__main__':
suite = unittest.TestLoader().discover('.')
def runtests():
unittest.TextTestRunner().run(suite)
cProfile.run('runtests()',sort='cumtime')
Performance Tests
25510 function calls (25298 primitive calls) in 0.820 seconds
Ordered by: cumulative time
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.820 0.820 <string>:1(<module>)
Performance Tests
def test_add_source_must_check_duplicated(self):
import time
psw = PhishingServiceWrapper()
before_functiion = time.time()
psw.add_url('http://www.testededominio.com.br', source_id=1)
after_functiion = time.time()
self.assertLess(after_functiion - before_function, 1.0)
Performance Tests@profile
def primes(n):
if n==2:
return [2]
elif n<2:
return []
s=range(3,n+1,2)
mroot = n ** 0.5
half=(n+1)/2-1
i=0
m=3
while m <= mroot:
if s[i]:
j=(m*m-3)/2
s[j]=0
while j<half:
s[j]=0
j+=m
i=i+1
m=2*i+3
return [2]+[x for x in s if x]
primes(100)
pip install line_profiler
kernprof -l –v primes.py
Performance Tests
Line # Hits Time Per Hit % Time Line Contents
==============================================================
2 @profile
3 def primes(n):
4 1 2 2.0 1.1 if n==2:
5 return [2]
6 1 1 1.0 0.5 elif n<2:
7 return []
8 1 4 4.0 2.1 s=range(3,n+1,2)
9 1 10 10.0 5.3 mroot = n ** 0.5
10 1 2 2.0 1.1 half=(n+1)/2-1
11 1 1 1.0 0.5 i=0
12 1 1 1.0 0.5 m=3
13 5 7 1.4 3.7 while m <= mroot:
14 4 4 1.0 2.1 if s[i]:
15 3 4 1.3 2.1 j=(m*m-3)/2
16 3 4 1.3 2.1 s[j]=0
17 31 31 1.0 16.3 while j<half:
18 28 28 1.0 14.7 s[j]=0
19 28 29 1.0 15.3 j+=m
20 4 4 1.0 2.1 i=i+1
21 4 4 1.0 2.1 m=2*i+3
22 50 54 1.1 28.4 return [2]+[x for x in s if x]
Performance Tests
python -m memory_profiler primes.py
pip install memory_profiler
Performance
2 @profile
3 7.9219 MB 0.0000 MB def primes(n):
4 7.9219 MB 0.0000 MB if n==2:
5 return [2]
6 7.9219 MB 0.0000 MB elif n<2:
7 return []
8 7.9219 MB 0.0000 MB s=range(3,n+1,2)
9 7.9258 MB 0.0039 MB mroot = n ** 0.5
10 7.9258 MB 0.0000 MB half=(n+1)/2-1
11 7.9258 MB 0.0000 MB i=0
12 7.9258 MB 0.0000 MB m=3
13 7.9297 MB 0.0039 MB while m <= mroot:
14 7.9297 MB 0.0000 MB if s[i]:
15 7.9297 MB 0.0000 MB j=(m*m-3)/2
16 7.9258 MB -0.0039 MB s[j]=0
17 7.9297 MB 0.0039 MB while j<half:
18 7.9297 MB 0.0000 MB s[j]=0
19 7.9297 MB 0.0000 MB j+=m
20 7.9297 MB 0.0000 MB i=i+1
21 7.9297 MB 0.0000 MB m=2*i+3
22 7.9297 MB 0.0000 MB return [2]+[x for x in s if x]
Bug Tracking
Bug Tracking
import unittest
from foobarbaz import Foo
class SimpleTestCase(unittest.TestCase):
def setUp(self):
"""Call before every test case.""“
self.foo = Foo()
self.file = open( "blah", "r" )
def tearDown(self):
"""Call after every test case.""“
self.file.close()
def test_get_client_name__with_big_param__must_return_False (self):
self.assertEqual(foo.get_client_name(“A” * 1024*1024) , False)
Analise estática
Analise estática
Checar idioma
Documentação de codigo
Compliance
Erros
Codigos duplicados ou complexos
pylint file.py
pip install pylint
Analise estática
Analise estática
radon
pip install radon
Complexidade ciclomática
Linhas de código, comentários, ...
Maintainability Index
Analise estática
Analise estática
Analise estática
Analise estática
Documentação
DocStrings
Documentação simples e clara, não deve conter detalhes da implementação
Documentação ruim é pior do que não documentado
Sumarizar o comportamento da função
Argumentos, retornos, exceções disparadas e restrições.
Não documentar o obvio
Documentação
import this
simple is better than complex
pep-0020
The Zen of Python
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
emiliocini@gmail.com
www.emiliosimoni.com

Qualidade levada a sério em Python - Emilio Simoni