qualidade levada a sério em python - emilio simoni
TRANSCRIPT
simple is better than complexThe 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 baixadoMaior empresa mobile LA200 mil downloads/dia
PessoasProcesso
Produto
Versionamento
QualidadeAutomatização
RequisitosMetodologia
Processo = Engenharia de software
Testes
Analise estática
Bug Tracking
Documentação
Teste
s au
tom
atiza
dos
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 Fixtureclass 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_RetornoEsperadoget_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_RetornoEsperadoget_client_name__with_invalid__params_must_return_False
import unittestfrom 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 Testsdef 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 Testsimport cProfileimport 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@profiledef 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 TestsLine # 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
Performance2 @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 Trackingimport unittestfrom 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áticaChecar idioma
Documentação de codigo
Compliance
Erros
Codigos duplicados ou complexos
pylint file.py
pip install pylint
Analise estática
Analise estática
radonpip 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çãoDocStrings
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 thissimple is better than complex
pep-0020The 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!