unittest

unittest unittest

unittest Pythonlink image 38

Aviso: Este post foi traduzido para o português usando um modelo de tradução automática. Por favor, me avise se encontrar algum erro.

Para realizar este post vamos a criar uma pasta chamada testing_python onde vamos criar todo o código

	
!mkdir testing_python
Copy

Dentro dessa pasta vamos criar as pastas src e tests onde vamos colocar o código fonte e os testes respectivamente.

	
!mkdir testing_python/src
!mkdir testing_python/tests
Copy

Bibliotecas necessáriaslink image 39

Para fazer os testes vamos usar a biblioteca unittest que vem por padrão no Python, mas além disso vamos instalar o coverage para poder ver a cobertura dos testes. A instalamos com Conda

conda install conda-forge::coverage
      

O com pip

pip install coverage
      

Primeiros testeslink image 40

Vamos a criar um primeiro arquivo chamado calculator.py na pasta src

	
!echo "def sum(a, b):" > testing_python/src/calculator.py
!echo " return a + b" >> testing_python/src/calculator.py
!echo "" >> testing_python/src/calculator.py
!echo "def substract(a, b):" >> testing_python/src/calculator.py
!echo " return a - b" >> testing_python/src/calculator.py
!echo "" >> testing_python/src/calculator.py
!echo "def multiply(a, b):" >> testing_python/src/calculator.py
!echo " return a * b" >> testing_python/src/calculator.py
!echo "" >> testing_python/src/calculator.py
!echo "def divide(a, b):" >> testing_python/src/calculator.py
!echo " return a / b" >> testing_python/src/calculator.py
Copy

Agora criamos um arquivo chamado test_calculator.py na pasta tests

	
!echo "import unittest" > testing_python/tests/test_calculator.py
!echo "from src.calculator import sum, substract, multiply, divide" >> testing_python/tests/test_calculator.py
!echo "" >> testing_python/tests/test_calculator.py
!echo "class TestCalculator(unittest.TestCase):" >> testing_python/tests/test_calculator.py
!echo " def test_sum(self):" >> testing_python/tests/test_calculator.py
!echo " self.assertEqual(sum(2, 2), 4)" >> testing_python/tests/test_calculator.py
!echo "" >> testing_python/tests/test_calculator.py
!echo " def test_substract(self):" >> testing_python/tests/test_calculator.py
!echo " self.assertEqual(substract(2, 1), 1)" >> testing_python/tests/test_calculator.py
!echo "" >> testing_python/tests/test_calculator.py
!echo " def test_multiply(self):" >> testing_python/tests/test_calculator.py
!echo " self.assertEqual(multiply(2, 3), 6)" >> testing_python/tests/test_calculator.py
!echo "" >> testing_python/tests/test_calculator.py
!echo " def test_divide(self):" >> testing_python/tests/test_calculator.py
!echo " self.assertEqual(divide(6, 3), 2)" >> testing_python/tests/test_calculator.py
Copy

Agora para executá-lo fazemos python -m unittest tests/test_calculator.py discover -s tests

	
!cd testing_python && python -m unittest tests/test_calculator.py
Copy
	
....
----------------------------------------------------------------------
Ran 4 tests in 0.000s
OK

Como podemos ver, aparecem quatro pontos pelos dois testes que foram executados e que foram corretos.

Vamos a modificar o arquivo de teste para provocar um erro, vamos fazer com que ao somar 2 e 2 nos dê 5

	
!echo "import unittest" > testing_python/tests/test_calculator.py
!echo "from src.calculator import sum, substract, multiply, divide" >> testing_python/tests/test_calculator.py
!echo "" >> testing_python/tests/test_calculator.py
!echo "class TestCalculator(unittest.TestCase):" >> testing_python/tests/test_calculator.py
!echo " def test_sum(self):" >> testing_python/tests/test_calculator.py
!echo " self.assertEqual(sum(2, 2), 5)" >> testing_python/tests/test_calculator.py
!echo "" >> testing_python/tests/test_calculator.py
!echo " def test_substract(self):" >> testing_python/tests/test_calculator.py
!echo " self.assertEqual(substract(2, 1), 1)" >> testing_python/tests/test_calculator.py
!echo "" >> testing_python/tests/test_calculator.py
!echo " def test_multiply(self):" >> testing_python/tests/test_calculator.py
!echo " self.assertEqual(multiply(2, 3), 6)" >> testing_python/tests/test_calculator.py
!echo "" >> testing_python/tests/test_calculator.py
!echo " def test_divide(self):" >> testing_python/tests/test_calculator.py
!echo " self.assertEqual(divide(6, 3), 2)" >> testing_python/tests/test_calculator.py
Copy

Agora rodamos os testes

	
!cd testing_python && python -m unittest tests/test_calculator.py
Copy
	
...F
======================================================================
FAIL: test_sum (tests.test_calculator.TestCalculator)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/wallabot/Documentos/web/portafolio/posts/testing_python/tests/test_calculator.py", line 6, in test_sum
self.assertEqual(sum(2, 2), 5)
AssertionError: 4 != 5
----------------------------------------------------------------------
Ran 4 tests in 0.000s
FAILED (failures=1)

Como podemos ver, agora aparece uma F que significa que um teste falhou, além disso, fornecemos a seguinte informação

FALHA: test_soma (tests.test_calculadora.TestCalculadora)
      ---------------------------------------------------------------------------
      Rastreio de chamada (mais recente por último):
      Arquivo "/home/wallabot/Documentos/web/portafolio/posts/testing_python/tests/test_calculator.py", linha 6, em test_sum
      self.assertEqual(sum(2, 2), 5)
      AssertionError: 4 != 5

Ele nos diz que o teste test_sum falhou na linha 6, que é a que modificamos, e que o resultado esperado era 5 e o obtido foi 4.

Uma coisa que não dissemos e que é importante, é que não chamamos os métodos test_sum e test_subtract diretamente, eles foram executados automaticamente. Isso se deve ao fato de que os métodos que começam com test_ são os que são executados automaticamente.

Uma forma mais simples de executar os testes é usar o comando discover que procura todos os arquivos que começam com test_ na pasta que passarmos pelo parâmetro -s

Primeiro reescrevemos bem os testes

	
!echo "import unittest" > testing_python/tests/test_calculator.py
!echo "from src.calculator import sum, substract, multiply, divide" >> testing_python/tests/test_calculator.py
!echo "" >> testing_python/tests/test_calculator.py
!echo "class TestCalculator(unittest.TestCase):" >> testing_python/tests/test_calculator.py
!echo " def test_sum(self):" >> testing_python/tests/test_calculator.py
!echo " self.assertEqual(sum(2, 2), 4)" >> testing_python/tests/test_calculator.py
!echo "" >> testing_python/tests/test_calculator.py
!echo " def test_substract(self):" >> testing_python/tests/test_calculator.py
!echo " self.assertEqual(substract(2, 1), 1)" >> testing_python/tests/test_calculator.py
!echo "" >> testing_python/tests/test_calculator.py
!echo " def test_multiply(self):" >> testing_python/tests/test_calculator.py
!echo " self.assertEqual(multiply(2, 3), 6)" >> testing_python/tests/test_calculator.py
!echo "" >> testing_python/tests/test_calculator.py
!echo " def test_divide(self):" >> testing_python/tests/test_calculator.py
!echo " self.assertEqual(divide(6, 3), 2)" >> testing_python/tests/test_calculator.py
Copy

E agora passamos os testes através de discover

	
!cd testing_python && python -m unittest discover -s tests
Copy
	
....
----------------------------------------------------------------------
Ran 4 tests in 0.000s
OK

Ele encontrou os testes e os passou corretamente

Configuração dos testeslink image 41

setUplink image 42

Com a biblioteca unittest podemos configurar os testes, mas para vê-los, primeiro vamos criar um novo arquivo de código chamado bank_account.py na pasta src

	
!echo "class BankAccount:" > testing_python/src/bank_account.py
!echo " def __init__(self, balance=0):" >> testing_python/src/bank_account.py
!echo " self.balance = balance" >> testing_python/src/bank_account.py
!echo "" >> testing_python/src/bank_account.py
!echo " def deposit(self, amount):" >> testing_python/src/bank_account.py
!echo " if amount > 0:" >> testing_python/src/bank_account.py
!echo " self.balance += amount" >> testing_python/src/bank_account.py
!echo " return self.balance" >> testing_python/src/bank_account.py
!echo "" >> testing_python/src/bank_account.py
!echo " def withdraw(self, amount):" >> testing_python/src/bank_account.py
!echo " if amount > 0:" >> testing_python/src/bank_account.py
!echo " self.balance -= amount" >> testing_python/src/bank_account.py
!echo " return self.balance" >> testing_python/src/bank_account.py
!echo "" >> testing_python/src/bank_account.py
!echo " def get_balance(self):" >> testing_python/src/bank_account.py
!echo " return self.balance" >> testing_python/src/bank_account.py
Copy

Agora adicionamos testes ao novo código, criando um arquivo chamado test_bank_account.py na pasta tests

Vamos a criar primeiro o teste para adicionar depósito, ou seja, o método deposit

	
!echo "import unittest" > testing_python/tests/test_bank_account.py
!echo "from src.bank_account import BankAccount" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo "class TestBankAccount(unittest.TestCase):" >> testing_python/tests/test_bank_account.py
!echo " def test_deposit(self):" >> testing_python/tests/test_bank_account.py
!echo " account = BankAccount()" >> testing_python/tests/test_bank_account.py
!echo " new_balace = account.deposit(1500)" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(new_balace, 1500)" >> testing_python/tests/test_bank_account.py
Copy

Passamos os testes

	
!cd testing_python && python -m unittest discover -s tests
Copy
	
.....
----------------------------------------------------------------------
Ran 5 tests in 0.000s
OK

Vemos que há cinco pontos, mas nós só escrevemos um teste, então usamos a flag -v para ver mais informações.

	
!cd testing_python && python -m unittest discover -s tests -v
Copy
	
test_deposit (test_bank_account.TestBankAccount) ... ok
test_divide (test_calculator.TestCalculator) ... ok
test_multiply (test_calculator.TestCalculator) ... ok
test_substract (test_calculator.TestCalculator) ... ok
test_sum (test_calculator.TestCalculator) ... ok
----------------------------------------------------------------------
Ran 5 tests in 0.000s
OK

Vemos que discover encontrou os testes test_calculator e test_bank_account e ambos passaram.

Vamos a criar os testes para o resto dos métodos

	
!echo "import unittest" > testing_python/tests/test_bank_account.py
!echo "from src.bank_account import BankAccount" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo "class TestBankAccount(unittest.TestCase):" >> testing_python/tests/test_bank_account.py
!echo " def test_deposit(self):" >> testing_python/tests/test_bank_account.py
!echo " account = BankAccount(balance=1000)" >> testing_python/tests/test_bank_account.py
!echo " new_balace = account.deposit(500)" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(new_balace, 1500)" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_withdraw(self):" >> testing_python/tests/test_bank_account.py
!echo " account = BankAccount(balance=1000)" >> testing_python/tests/test_bank_account.py
!echo " new_balace = account.withdraw(200)" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(new_balace, 800)" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_get_balance(self):" >> testing_python/tests/test_bank_account.py
!echo " account = BankAccount(balance=1000)" >> testing_python/tests/test_bank_account.py
!echo " balance = account.get_balance()" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(balance, 1000)" >> testing_python/tests/test_bank_account.py
Copy

Passamos os testes

	
!cd testing_python && python -m unittest discover -s tests -v
Copy
	
test_deposit (test_bank_account.TestBankAccount) ... ok
test_get_balance (test_bank_account.TestBankAccount) ... ok
test_withdraw (test_bank_account.TestBankAccount) ... ok
test_divide (test_calculator.TestCalculator) ... ok
test_multiply (test_calculator.TestCalculator) ... ok
test_substract (test_calculator.TestCalculator) ... ok
test_sum (test_calculator.TestCalculator) ... ok
----------------------------------------------------------------------
Ran 7 tests in 0.000s
OK

Vemos que passaram satisfatoriamente. Agora vejamos uma coisa, em todos os testes fizemos account = BankAccount(balance=1000) e depois chamamos os métodos, isso é porque cada teste é executado em um novo objeto, ou seja, os objetos não são compartilhados entre os testes.

Então podemos usar o método setUp para criar um objeto que seja compartilhado entre todos os testes.

	
!echo "import unittest" > testing_python/tests/test_bank_account.py
!echo "from src.bank_account import BankAccount" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo "class TestBankAccount(unittest.TestCase):" >> testing_python/tests/test_bank_account.py
!echo " def setUp(self):" >> testing_python/tests/test_bank_account.py
!echo " self.account = BankAccount(balance=1000)" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_deposit(self):" >> testing_python/tests/test_bank_account.py
!echo " new_balace = self.account.deposit(500)" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(new_balace, 1500)" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_withdraw(self):" >> testing_python/tests/test_bank_account.py
!echo " new_balace = self.account.withdraw(200)" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(new_balace, 800)" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_get_balance(self):" >> testing_python/tests/test_bank_account.py
!echo " balance = self.account.get_balance()" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(balance, 1000)" >> testing_python/tests/test_bank_account.py
Copy

Como podemos ver, criamos a conta no método setUp e removemos a criação da conta nos testes. Vamos rodar os testes.

	
!cd testing_python && python -m unittest discover -s tests -v
Copy
	
test_deposit (test_bank_account.TestBankAccount) ... ok
test_get_balance (test_bank_account.TestBankAccount) ... ok
test_withdraw (test_bank_account.TestBankAccount) ... ok
test_divide (test_calculator.TestCalculator) ... ok
test_multiply (test_calculator.TestCalculator) ... ok
test_substract (test_calculator.TestCalculator) ... ok
test_sum (test_calculator.TestCalculator) ... ok
----------------------------------------------------------------------
Ran 7 tests in 0.000s
OK

tearDownlink image 43

Assim como com o método setUp configuramos o ambiente antes de executar os testes, com o método tearDown podemos limpar o ambiente após executar os testes. Para testá-lo vamos adicionar ao código de bank_account.py que as operações sejam escritas em um arquivo de log.

	
!echo "class BankAccount:" > testing_python/src/bank_account.py
!echo " def __init__(self, balance=0, log_file=None):" >> testing_python/src/bank_account.py
!echo " self.balance = balance" >> testing_python/src/bank_account.py
!echo " self.log_file = log_file" >> testing_python/src/bank_account.py
!echo " self._log_transaction('Account created')" >> testing_python/src/bank_account.py
!echo "" >> testing_python/src/bank_account.py
!echo " def _log_transaction(self, message):" >> testing_python/src/bank_account.py
!echo " if self.log_file:" >> testing_python/src/bank_account.py
!echo " with open(self.log_file, 'a') as file:" >> testing_python/src/bank_account.py
!echo " file.write(f'{message}\\ ')" >> testing_python/src/bank_account.py
!echo "" >> testing_python/src/bank_account.py
!echo " def deposit(self, amount):" >> testing_python/src/bank_account.py
!echo " if amount > 0:" >> testing_python/src/bank_account.py
!echo " self.balance += amount" >> testing_python/src/bank_account.py
!echo " self._log_transaction(f'Deposit {amount}, new balance {self.balance}')" >> testing_python/src/bank_account.py
!echo " return self.balance" >> testing_python/src/bank_account.py
!echo "" >> testing_python/src/bank_account.py
!echo " def withdraw(self, amount):" >> testing_python/src/bank_account.py
!echo " if amount > 0:" >> testing_python/src/bank_account.py
!echo " self.balance -= amount" >> testing_python/src/bank_account.py
!echo " self._log_transaction(f'Withdraw {amount}, new balance {self.balance}')" >> testing_python/src/bank_account.py
!echo " return self.balance" >> testing_python/src/bank_account.py
!echo "" >> testing_python/src/bank_account.py
!echo " def get_balance(self):" >> testing_python/src/bank_account.py
!echo " self._log_transaction(f'Balance check, balance {self.balance}')" >> testing_python/src/bank_account.py
!echo " return self.balance" >> testing_python/src/bank_account.py
Copy

Agora adicionamos um teste ao novo método _log_transaction

	
!echo "import unittest" > testing_python/tests/test_bank_account.py
!echo "import os" >> testing_python/tests/test_bank_account.py
!echo "from src.bank_account import BankAccount" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo "class TestBankAccount(unittest.TestCase):" >> testing_python/tests/test_bank_account.py
!echo " def setUp(self):" >> testing_python/tests/test_bank_account.py
!echo " self.account = BankAccount(balance=1000, log_file='test_log.txt')" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_deposit(self):" >> testing_python/tests/test_bank_account.py
!echo " new_balace = self.account.deposit(500)" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(new_balace, 1500)" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_withdraw(self):" >> testing_python/tests/test_bank_account.py
!echo " new_balace = self.account.withdraw(200)" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(new_balace, 800)" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_get_balance(self):" >> testing_python/tests/test_bank_account.py
!echo " balance = self.account.get_balance()" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(balance, 1000)" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_transaction_log(self):" >> testing_python/tests/test_bank_account.py
!echo " self.account.deposit(500)" >> testing_python/tests/test_bank_account.py
!echo " self.account.withdraw(200)" >> testing_python/tests/test_bank_account.py
!echo " self.account.get_balance()" >> testing_python/tests/test_bank_account.py
!echo " assert os.path.exists('test_log.txt')" >> testing_python/tests/test_bank_account.py
!echo " with open('test_log.txt', 'r') as file:" >> testing_python/tests/test_bank_account.py
!echo " content = file.readlines()" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(content, [" >> testing_python/tests/test_bank_account.py
!echo " 'Account created\\n', " >> testing_python/tests/test_bank_account.py
!echo " 'Deposit 500, new balance 1500\\n', " >> testing_python/tests/test_bank_account.py
!echo " 'Account created\\n', " >> testing_python/tests/test_bank_account.py
!echo " 'Balance check, balance 1000\\n', " >> testing_python/tests/test_bank_account.py
!echo " 'Account created\\n', " >> testing_python/tests/test_bank_account.py
!echo " 'Deposit 500, new balance 1500\\n', " >> testing_python/tests/test_bank_account.py
!echo " 'Withdraw 200, new balance 1300\\n', " >> testing_python/tests/test_bank_account.py
!echo " 'Balance check, balance 1300\\n'])" >> testing_python/tests/test_bank_account.py
Copy

Passamos os testes

	
!cd testing_python && python -m unittest discover -s tests -v
Copy
	
test_deposit (test_bank_account.TestBankAccount) ... ok
test_get_balance (test_bank_account.TestBankAccount) ... ok
test_transaction_log (test_bank_account.TestBankAccount) ... FAIL
test_withdraw (test_bank_account.TestBankAccount) ... ok
test_divide (test_calculator.TestCalculator) ... ok
test_multiply (test_calculator.TestCalculator) ... ok
test_substract (test_calculator.TestCalculator) ... ok
test_sum (test_calculator.TestCalculator) ... ok
======================================================================
FAIL: test_transaction_log (test_bank_account.TestBankAccount)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/wallabot/Documentos/web/portafolio/posts/testing_python/tests/test_bank_account.py", line 28, in test_transaction_log
self.assertEqual(content, [
AssertionError: Lists differ: ['Acc[224 chars]00 ', 'Account created ', 'Withdraw 200, new[246 chars]0 '] != ['Acc[224 chars]00 ']
First list contains 10 additional elements.
First extra element 8:
'Account created '
['Account created ',
'Deposit 500, new balance 1500 ',
'Account created ',
'Balance check, balance 1000 ',
'Account created ',
'Deposit 500, new balance 1500 ',
'Withdraw 200, new balance 1300 ',
- 'Balance check, balance 1300 ',
- 'Account created ',
- 'Withdraw 200, new balance 800 ',
- 'Account created ',
- 'Deposit 500, new balance 1500 ',
- 'Account created ',
- 'Balance check, balance 1000 ',
- 'Account created ',
- 'Deposit 500, new balance 1500 ',
- 'Withdraw 200, new balance 1300 ',
'Balance check, balance 1300 ']
----------------------------------------------------------------------
Ran 8 tests in 0.001s
FAILED (failures=1)

Os testes saíram bem, mas vemos que no arquivo de log há muitas linhas com o texto Account created, isso é porque no início de cada teste é executado o método setUp que cria uma conta, por isso precisamos criar o método tearDown para eliminar o arquivo de log após cada teste.

Como é um arquivo gerado para o teste, não deveria existir após executar os testes, então vamos adicionar o método tearDown para apagar o arquivo

	
!echo "import unittest" > testing_python/tests/test_bank_account.py
!echo "import os" >> testing_python/tests/test_bank_account.py
!echo "from src.bank_account import BankAccount" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo "class TestBankAccount(unittest.TestCase):" >> testing_python/tests/test_bank_account.py
!echo " def setUp(self):" >> testing_python/tests/test_bank_account.py
!echo " self.account = BankAccount(balance=1000, log_file='test_log.txt')" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def tearDown(self):" >> testing_python/tests/test_bank_account.py
!echo " if os.path.exists('test_log.txt'):" >> testing_python/tests/test_bank_account.py
!echo " os.remove('test_log.txt')" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_deposit(self):" >> testing_python/tests/test_bank_account.py
!echo " new_balace = self.account.deposit(500)" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(new_balace, 1500)" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_withdraw(self):" >> testing_python/tests/test_bank_account.py
!echo " new_balace = self.account.withdraw(200)" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(new_balace, 800)" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_get_balance(self):" >> testing_python/tests/test_bank_account.py
!echo " balance = self.account.get_balance()" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(balance, 1000)" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_transaction_log(self):" >> testing_python/tests/test_bank_account.py
!echo " self.account.deposit(500)" >> testing_python/tests/test_bank_account.py
!echo " self.account.withdraw(200)" >> testing_python/tests/test_bank_account.py
!echo " self.account.get_balance()" >> testing_python/tests/test_bank_account.py
!echo " assert os.path.exists('test_log.txt')" >> testing_python/tests/test_bank_account.py
!echo " with open('test_log.txt', 'r') as file:" >> testing_python/tests/test_bank_account.py
!echo " content = file.readlines()" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(content, [" >> testing_python/tests/test_bank_account.py
!echo " 'Account created\\n', " >> testing_python/tests/test_bank_account.py
!echo " 'Deposit 500, new balance 1500\\n', " >> testing_python/tests/test_bank_account.py
!echo " 'Account created\\n', " >> testing_python/tests/test_bank_account.py
!echo " 'Balance check, balance 1000\\n', " >> testing_python/tests/test_bank_account.py
!echo " 'Account created\\n', " >> testing_python/tests/test_bank_account.py
!echo " 'Deposit 500, new balance 1500\\n', " >> testing_python/tests/test_bank_account.py
!echo " 'Withdraw 200, new balance 1300\\n', " >> testing_python/tests/test_bank_account.py
!echo " 'Balance check, balance 1300\\n'" >> testing_python/tests/test_bank_account.py
!echo " ])" >> testing_python/tests/test_bank_account.py
Copy

Voltamos a passar os testes

	
!cd testing_python && python -m unittest discover -s tests -v
Copy
	
test_deposit (test_bank_account.TestBankAccount) ... ok
test_get_balance (test_bank_account.TestBankAccount) ... ok
test_transaction_log (test_bank_account.TestBankAccount) ... FAIL
test_withdraw (test_bank_account.TestBankAccount) ... ok
test_divide (test_calculator.TestCalculator) ... ok
test_multiply (test_calculator.TestCalculator) ... ok
test_substract (test_calculator.TestCalculator) ... ok
test_sum (test_calculator.TestCalculator) ... ok
======================================================================
FAIL: test_transaction_log (test_bank_account.TestBankAccount)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/wallabot/Documentos/web/portafolio/posts/testing_python/tests/test_bank_account.py", line 32, in test_transaction_log
self.assertEqual(content, [
AssertionError: Lists differ: ['Acc[48 chars]n', 'Withdraw 200, new balance 1300 ', 'Balan[21 chars]0 '] != ['Acc[48 chars]n', 'Account created ', 'Balance check, balan[131 chars]0 ']
First differing element 2:
'Withdraw 200, new balance 1300 '
'Account created '
Second list contains 4 additional elements.
First extra element 4:
'Account created '
['Account created ',
+ 'Deposit 500, new balance 1500 ',
+ 'Account created ',
+ 'Balance check, balance 1000 ',
+ 'Account created ',
'Deposit 500, new balance 1500 ',
'Withdraw 200, new balance 1300 ',
'Balance check, balance 1300 ']
----------------------------------------------------------------------
Ran 8 tests in 0.001s
FAILED (failures=1)

Mas agora estamos recebendo um erro, porque, ao final de cada teste, temos excluído o arquivo de log, então não precisamos verificar se foi escrito tanto texto, mas apenas o do teste que estamos realizando. Então vamos modificar o teste para que verifique apenas se foi escrito o texto do teste.

	
!echo "import unittest" > testing_python/tests/test_bank_account.py
!echo "import os" >> testing_python/tests/test_bank_account.py
!echo "from src.bank_account import BankAccount" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo "class TestBankAccount(unittest.TestCase):" >> testing_python/tests/test_bank_account.py
!echo " def setUp(self):" >> testing_python/tests/test_bank_account.py
!echo " self.account = BankAccount(balance=1000, log_file='test_log.txt')" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def tearDown(self):" >> testing_python/tests/test_bank_account.py
!echo " if os.path.exists('test_log.txt'):" >> testing_python/tests/test_bank_account.py
!echo " os.remove('test_log.txt')" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_deposit(self):" >> testing_python/tests/test_bank_account.py
!echo " new_balace = self.account.deposit(500)" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(new_balace, 1500)" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_withdraw(self):" >> testing_python/tests/test_bank_account.py
!echo " new_balace = self.account.withdraw(200)" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(new_balace, 800)" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_get_balance(self):" >> testing_python/tests/test_bank_account.py
!echo " balance = self.account.get_balance()" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(balance, 1000)" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_transaction_log(self):" >> testing_python/tests/test_bank_account.py
!echo " self.account.deposit(500)" >> testing_python/tests/test_bank_account.py
!echo " self.account.withdraw(200)" >> testing_python/tests/test_bank_account.py
!echo " self.account.get_balance()" >> testing_python/tests/test_bank_account.py
!echo " assert os.path.exists('test_log.txt')" >> testing_python/tests/test_bank_account.py
!echo " with open('test_log.txt', 'r') as file:" >> testing_python/tests/test_bank_account.py
!echo " content = file.readlines()" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(content, [" >> testing_python/tests/test_bank_account.py
!echo " 'Account created\\n', " >> testing_python/tests/test_bank_account.py
!echo " 'Deposit 500, new balance 1500\\n', " >> testing_python/tests/test_bank_account.py
!echo " 'Withdraw 200, new balance 1300\\n', " >> testing_python/tests/test_bank_account.py
!echo " 'Balance check, balance 1300\\n'" >> testing_python/tests/test_bank_account.py
!echo " ])" >> testing_python/tests/test_bank_account.py
Copy

E passamos os testes

	
!cd testing_python && python -m unittest discover -s tests -v
Copy
	
test_deposit (test_bank_account.TestBankAccount) ... ok
test_get_balance (test_bank_account.TestBankAccount) ... ok
test_transaction_log (test_bank_account.TestBankAccount) ... ok
test_withdraw (test_bank_account.TestBankAccount) ... ok
test_divide (test_calculator.TestCalculator) ... ok
test_multiply (test_calculator.TestCalculator) ... ok
test_substract (test_calculator.TestCalculator) ... ok
test_sum (test_calculator.TestCalculator) ... ok
----------------------------------------------------------------------
Ran 8 tests in 0.001s
OK

Documentação de erroslink image 44

Se você notou, até agora nos testes estávamos usando assertEqual. Este método nos dá a opção de escrever uma mensagem de erro quando a condição não é atendida. Vamos modificar um teste para que ele falhe e ver a mensagem de erro.

	
!echo "import unittest" > testing_python/tests/test_bank_account.py
!echo "import os" >> testing_python/tests/test_bank_account.py
!echo "from src.bank_account import BankAccount" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo "class TestBankAccount(unittest.TestCase):" >> testing_python/tests/test_bank_account.py
!echo " def setUp(self):" >> testing_python/tests/test_bank_account.py
!echo " self.account = BankAccount(balance=1000, log_file='test_log.txt')" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def tearDown(self):" >> testing_python/tests/test_bank_account.py
!echo " if os.path.exists('test_log.txt'):" >> testing_python/tests/test_bank_account.py
!echo " os.remove('test_log.txt')" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_deposit(self):" >> testing_python/tests/test_bank_account.py
!echo " new_balace = self.account.deposit(500)" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(new_balace, 1500)" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_withdraw(self):" >> testing_python/tests/test_bank_account.py
!echo " new_balace = self.account.withdraw(200)" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(new_balace, 500, 'Balance is not correct')" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_get_balance(self):" >> testing_python/tests/test_bank_account.py
!echo " balance = self.account.get_balance()" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(balance, 1000)" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_transaction_log(self):" >> testing_python/tests/test_bank_account.py
!echo " self.account.deposit(500)" >> testing_python/tests/test_bank_account.py
!echo " self.account.withdraw(200)" >> testing_python/tests/test_bank_account.py
!echo " self.account.get_balance()" >> testing_python/tests/test_bank_account.py
!echo " assert os.path.exists('test_log.txt')" >> testing_python/tests/test_bank_account.py
!echo " with open('test_log.txt', 'r') as file:" >> testing_python/tests/test_bank_account.py
!echo " content = file.readlines()" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(content, [" >> testing_python/tests/test_bank_account.py
!echo " 'Account created\\n', " >> testing_python/tests/test_bank_account.py
!echo " 'Deposit 500, new balance 1500\\n', " >> testing_python/tests/test_bank_account.py
!echo " 'Withdraw 200, new balance 1300\\n', " >> testing_python/tests/test_bank_account.py
!echo " 'Balance check, balance 1300\\n'" >> testing_python/tests/test_bank_account.py
!echo " ])" >> testing_python/tests/test_bank_account.py
Copy

Passamos os testes

	
!cd testing_python && python -m unittest discover -s tests -v
Copy
	
test_deposit (test_bank_account.TestBankAccount) ... ok
test_get_balance (test_bank_account.TestBankAccount) ... ok
test_transaction_log (test_bank_account.TestBankAccount) ... ok
test_withdraw (test_bank_account.TestBankAccount) ... FAIL
test_divide (test_calculator.TestCalculator) ... ok
test_multiply (test_calculator.TestCalculator) ... ok
test_substract (test_calculator.TestCalculator) ... ok
test_sum (test_calculator.TestCalculator) ... ok
======================================================================
FAIL: test_withdraw (test_bank_account.TestBankAccount)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/wallabot/Documentos/web/portafolio/posts/testing_python/tests/test_bank_account.py", line 19, in test_withdraw
self.assertEqual(new_balace, 500, 'Balance is not correct')
AssertionError: 800 != 500 : Balance is not correct
----------------------------------------------------------------------
Ran 8 tests in 0.001s
FAILED (failures=1)

Como podemos ver, temos a mensagem que escrevemos no teste AssertionError: 800 != 500 : Balance is not correct

assertslink image 45

Até agora usamos os assertEqual, mas há mais, como por exemplo o assertTrue. Vamos modificar a linha assert os.path.exists('test_log.txt') para self.assertTrue(os.path.exists('test_log.txt')).

	
!echo "import unittest" > testing_python/tests/test_bank_account.py
!echo "import os" >> testing_python/tests/test_bank_account.py
!echo "from src.bank_account import BankAccount" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo "class TestBankAccount(unittest.TestCase):" >> testing_python/tests/test_bank_account.py
!echo " def setUp(self):" >> testing_python/tests/test_bank_account.py
!echo " self.account = BankAccount(balance=1000, log_file='test_log.txt')" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def tearDown(self):" >> testing_python/tests/test_bank_account.py
!echo " if os.path.exists('test_log.txt'):" >> testing_python/tests/test_bank_account.py
!echo " os.remove('test_log.txt')" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_deposit(self):" >> testing_python/tests/test_bank_account.py
!echo " new_balace = self.account.deposit(500)" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(new_balace, 1500)" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_withdraw(self):" >> testing_python/tests/test_bank_account.py
!echo " new_balace = self.account.withdraw(200)" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(new_balace, 800, 'Balance is not correct')" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_get_balance(self):" >> testing_python/tests/test_bank_account.py
!echo " balance = self.account.get_balance()" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(balance, 1000)" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_transaction_log(self):" >> testing_python/tests/test_bank_account.py
!echo " self.account.deposit(500)" >> testing_python/tests/test_bank_account.py
!echo " self.account.withdraw(200)" >> testing_python/tests/test_bank_account.py
!echo " self.account.get_balance()" >> testing_python/tests/test_bank_account.py
!echo " self.assertTrue(os.path.exists('test_log.txt'))" >> testing_python/tests/test_bank_account.py
!echo " with open('test_log.txt', 'r') as file:" >> testing_python/tests/test_bank_account.py
!echo " content = file.readlines()" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(content, [" >> testing_python/tests/test_bank_account.py
!echo " 'Account created\\n', " >> testing_python/tests/test_bank_account.py
!echo " 'Deposit 500, new balance 1500\\n', " >> testing_python/tests/test_bank_account.py
!echo " 'Withdraw 200, new balance 1300\\n', " >> testing_python/tests/test_bank_account.py
!echo " 'Balance check, balance 1300\\n'" >> testing_python/tests/test_bank_account.py
!echo " ])" >> testing_python/tests/test_bank_account.py
Copy

E passamos os testes

	
!cd testing_python && python -m unittest discover -s tests -v
Copy
	
test_deposit (test_bank_account.TestBankAccount) ... ok
test_get_balance (test_bank_account.TestBankAccount) ... ok
test_transaction_log (test_bank_account.TestBankAccount) ... ok
test_withdraw (test_bank_account.TestBankAccount) ... ok
test_divide (test_calculator.TestCalculator) ... ok
test_multiply (test_calculator.TestCalculator) ... ok
test_substract (test_calculator.TestCalculator) ... ok
test_sum (test_calculator.TestCalculator) ... ok
----------------------------------------------------------------------
Ran 8 tests in 0.001s
OK

Alguns outros asserts que podemos usar são:

  • assertEqual
  • assertNotEqual
  • assertTrue
  • assertFalse
  • assertRaises

skiplink image 46

Assim como em Python podemos usar pass para não fazer nada, em testes podemos usar skip para saltar um teste. Isso pode ser útil quando sabemos que queremos passar algumas provas, mas ainda não temos o código para isso.

Vou adicionar um teste para verificar se o usuário tem um empréstimo, mas como ainda não há código relacionado aos empréstimos, vou pular o teste.

	
!echo "import unittest" > testing_python/tests/test_bank_account.py
!echo "import os" >> testing_python/tests/test_bank_account.py
!echo "from src.bank_account import BankAccount" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo "class TestBankAccount(unittest.TestCase):" >> testing_python/tests/test_bank_account.py
!echo " def setUp(self):" >> testing_python/tests/test_bank_account.py
!echo " self.account = BankAccount(balance=1000, log_file='test_log.txt')" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def tearDown(self):" >> testing_python/tests/test_bank_account.py
!echo " if os.path.exists('test_log.txt'):" >> testing_python/tests/test_bank_account.py
!echo " os.remove('test_log.txt')" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_deposit(self):" >> testing_python/tests/test_bank_account.py
!echo " new_balace = self.account.deposit(500)" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(new_balace, 1500)" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_withdraw(self):" >> testing_python/tests/test_bank_account.py
!echo " new_balace = self.account.withdraw(200)" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(new_balace, 800, 'Balance is not correct')" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_get_balance(self):" >> testing_python/tests/test_bank_account.py
!echo " balance = self.account.get_balance()" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(balance, 1000)" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_transaction_log(self):" >> testing_python/tests/test_bank_account.py
!echo " self.account.deposit(500)" >> testing_python/tests/test_bank_account.py
!echo " self.account.withdraw(200)" >> testing_python/tests/test_bank_account.py
!echo " self.account.get_balance()" >> testing_python/tests/test_bank_account.py
!echo " self.assertTrue(os.path.exists('test_log.txt'))" >> testing_python/tests/test_bank_account.py
!echo " with open('test_log.txt', 'r') as file:" >> testing_python/tests/test_bank_account.py
!echo " content = file.readlines()" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(content, [" >> testing_python/tests/test_bank_account.py
!echo " 'Account created\\n', " >> testing_python/tests/test_bank_account.py
!echo " 'Deposit 500, new balance 1500\\n', " >> testing_python/tests/test_bank_account.py
!echo " 'Withdraw 200, new balance 1300\\n', " >> testing_python/tests/test_bank_account.py
!echo " 'Balance check, balance 1300\\n'" >> testing_python/tests/test_bank_account.py
!echo " ])" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " @unittest.skip('Not implemented yet')" >> testing_python/tests/test_bank_account.py
!echo " def test_has_loan(self):" >> testing_python/tests/test_bank_account.py
!echo " self.assertFalse(self.account.has_loan())" >> testing_python/tests/test_bank_account.py
Copy

Vamos ver o que acontece ao executá-lo

	
!cd testing_python && python -m unittest discover -s tests -v
Copy
	
test_deposit (test_bank_account.TestBankAccount) ... ok
test_get_balance (test_bank_account.TestBankAccount) ... ok
test_has_loan (test_bank_account.TestBankAccount) ... skipped 'Not implemented yet'
test_transaction_log (test_bank_account.TestBankAccount) ... ok
test_withdraw (test_bank_account.TestBankAccount) ... ok
test_divide (test_calculator.TestCalculator) ... ok
test_multiply (test_calculator.TestCalculator) ... ok
test_substract (test_calculator.TestCalculator) ... ok
test_sum (test_calculator.TestCalculator) ... ok
----------------------------------------------------------------------
Ran 9 tests in 0.001s
OK (skipped=1)

Podemos ver test_has_loan (test_bank_account.TestBankAccount) ... skipped 'Not implemented yet' que esse teste foi pulado como queríamos.

skipIflink image 47

Outra opção é usar skipIf para pular um teste se uma condição for atendida. Vou adicionar uma variável no início chamada server para saber se estamos em um ambiente local ou no servidor, e o teste verificará essa variável, de modo que, se estivermos no servidor, o teste será pulado.

	
!echo "import unittest" > testing_python/tests/test_bank_account.py
!echo "import os" >> testing_python/tests/test_bank_account.py
!echo "from src.bank_account import BankAccount" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo "SERVER = True" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo "class TestBankAccount(unittest.TestCase):" >> testing_python/tests/test_bank_account.py
!echo " def setUp(self):" >> testing_python/tests/test_bank_account.py
!echo " self.account = BankAccount(balance=1000, log_file='test_log.txt')" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def tearDown(self):" >> testing_python/tests/test_bank_account.py
!echo " if os.path.exists('test_log.txt'):" >> testing_python/tests/test_bank_account.py
!echo " os.remove('test_log.txt')" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_deposit(self):" >> testing_python/tests/test_bank_account.py
!echo " new_balace = self.account.deposit(500)" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(new_balace, 1500)" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_withdraw(self):" >> testing_python/tests/test_bank_account.py
!echo " new_balace = self.account.withdraw(200)" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(new_balace, 800, 'Balance is not correct')" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_get_balance(self):" >> testing_python/tests/test_bank_account.py
!echo " balance = self.account.get_balance()" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(balance, 1000)" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_transaction_log(self):" >> testing_python/tests/test_bank_account.py
!echo " self.account.deposit(500)" >> testing_python/tests/test_bank_account.py
!echo " self.account.withdraw(200)" >> testing_python/tests/test_bank_account.py
!echo " self.account.get_balance()" >> testing_python/tests/test_bank_account.py
!echo " self.assertTrue(os.path.exists('test_log.txt'))" >> testing_python/tests/test_bank_account.py
!echo " with open('test_log.txt', 'r') as file:" >> testing_python/tests/test_bank_account.py
!echo " content = file.readlines()" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(content, [" >> testing_python/tests/test_bank_account.py
!echo " 'Account created\\n', " >> testing_python/tests/test_bank_account.py
!echo " 'Deposit 500, new balance 1500\\n', " >> testing_python/tests/test_bank_account.py
!echo " 'Withdraw 200, new balance 1300\\n', " >> testing_python/tests/test_bank_account.py
!echo " 'Balance check, balance 1300\\n'" >> testing_python/tests/test_bank_account.py
!echo " ])" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " @unittest.skip('Not implemented yet')" >> testing_python/tests/test_bank_account.py
!echo " def test_has_loan(self):" >> testing_python/tests/test_bank_account.py
!echo " self.assertFalse(self.account.has_loan())" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " @unittest.skipIf(SERVER, 'Only for local testing')" >> testing_python/tests/test_bank_account.py
!echo " def test_server(self):" >> testing_python/tests/test_bank_account.py
!echo " self.assertTrue(SERVER)" >> testing_python/tests/test_bank_account.py
Copy

Se passarmos nos testes

	
!cd testing_python && python -m unittest discover -s tests -v
Copy
	
test_deposit (test_bank_account.TestBankAccount) ... ok
test_get_balance (test_bank_account.TestBankAccount) ... ok
test_has_loan (test_bank_account.TestBankAccount) ... skipped 'Not implemented yet'
test_server (test_bank_account.TestBankAccount) ... skipped 'Only for local testing'
test_transaction_log (test_bank_account.TestBankAccount) ... ok
test_withdraw (test_bank_account.TestBankAccount) ... ok
test_divide (test_calculator.TestCalculator) ... ok
test_multiply (test_calculator.TestCalculator) ... ok
test_substract (test_calculator.TestCalculator) ... ok
test_sum (test_calculator.TestCalculator) ... ok
----------------------------------------------------------------------
Ran 10 tests in 0.001s
OK (skipped=2)

Vemos test_server (test_bank_account.TestBankAccount) ... skipped 'Only for local testing' já que, como SERVER = True, o teste foi pulado.

falhaEsperadalink image 48

Deve ocorrer um erro quando um usuário tenta sacar mais dinheiro do que possui, de modo que com o método expectedFailure podemos indicar que esperamos que o teste falhe.

	
!echo "import unittest" > testing_python/tests/test_bank_account.py
!echo "import os" >> testing_python/tests/test_bank_account.py
!echo "from src.bank_account import BankAccount" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo "SERVER = True" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo "class TestBankAccount(unittest.TestCase):" >> testing_python/tests/test_bank_account.py
!echo " def setUp(self):" >> testing_python/tests/test_bank_account.py
!echo " self.account = BankAccount(balance=1000, log_file='test_log.txt')" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def tearDown(self):" >> testing_python/tests/test_bank_account.py
!echo " if os.path.exists('test_log.txt'):" >> testing_python/tests/test_bank_account.py
!echo " os.remove('test_log.txt')" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_deposit(self):" >> testing_python/tests/test_bank_account.py
!echo " new_balace = self.account.deposit(500)" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(new_balace, 1500)" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_withdraw(self):" >> testing_python/tests/test_bank_account.py
!echo " new_balace = self.account.withdraw(200)" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(new_balace, 800, 'Balance is not correct')" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_get_balance(self):" >> testing_python/tests/test_bank_account.py
!echo " balance = self.account.get_balance()" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(balance, 1000)" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_transaction_log(self):" >> testing_python/tests/test_bank_account.py
!echo " self.account.deposit(500)" >> testing_python/tests/test_bank_account.py
!echo " self.account.withdraw(200)" >> testing_python/tests/test_bank_account.py
!echo " self.account.get_balance()" >> testing_python/tests/test_bank_account.py
!echo " self.assertTrue(os.path.exists('test_log.txt'))" >> testing_python/tests/test_bank_account.py
!echo " with open('test_log.txt', 'r') as file:" >> testing_python/tests/test_bank_account.py
!echo " content = file.readlines()" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(content, [" >> testing_python/tests/test_bank_account.py
!echo " 'Account created\\n', " >> testing_python/tests/test_bank_account.py
!echo " 'Deposit 500, new balance 1500\\n', " >> testing_python/tests/test_bank_account.py
!echo " 'Withdraw 200, new balance 1300\\n', " >> testing_python/tests/test_bank_account.py
!echo " 'Balance check, balance 1300\\n'" >> testing_python/tests/test_bank_account.py
!echo " ])" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " @unittest.skip('Not implemented yet')" >> testing_python/tests/test_bank_account.py
!echo " def test_has_loan(self):" >> testing_python/tests/test_bank_account.py
!echo " self.assertFalse(self.account.has_loan())" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " @unittest.skipIf(SERVER, 'Only for local testing')" >> testing_python/tests/test_bank_account.py
!echo " def test_server(self):" >> testing_python/tests/test_bank_account.py
!echo " self.assertTrue(SERVER)" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " @unittest.expectedFailure" >> testing_python/tests/test_bank_account.py
!echo " def test_fail(self):" >> testing_python/tests/test_bank_account.py
!echo " new_balace = self.account.withdraw(1200)" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(new_balace, 800, 'Not enough money')" >> testing_python/tests/test_bank_account.py
Copy

Passamos os testes

	
!cd testing_python && python -m unittest discover -s tests -v
Copy
	
test_deposit (test_bank_account.TestBankAccount) ... ok
test_fail (test_bank_account.TestBankAccount) ... expected failure
test_get_balance (test_bank_account.TestBankAccount) ... ok
test_has_loan (test_bank_account.TestBankAccount) ... skipped 'Not implemented yet'
test_server (test_bank_account.TestBankAccount) ... skipped 'Only for local testing'
test_transaction_log (test_bank_account.TestBankAccount) ... ok
test_withdraw (test_bank_account.TestBankAccount) ... ok
test_divide (test_calculator.TestCalculator) ... ok
test_multiply (test_calculator.TestCalculator) ... ok
test_substract (test_calculator.TestCalculator) ... ok
test_sum (test_calculator.TestCalculator) ... ok
----------------------------------------------------------------------
Ran 11 tests in 0.001s
OK (skipped=2, expected failures=1)

Como vemos, obtemos test_fail (test_bank_account.TestBankAccount) ... expected failure porque é um erro que esperávamos

skipUnlesslink image 49

Assim como antes, pode haver testes que queiramos fazer apenas no ambiente de desenvolvimento, mas nunca no servidor, ou em staging, ou em produção. Para isso, podemos usar skipUnless

	
!echo "import unittest" > testing_python/tests/test_bank_account.py
!echo "import os" >> testing_python/tests/test_bank_account.py
!echo "from src.bank_account import BankAccount" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo "SERVER = True" >> testing_python/tests/test_bank_account.py
!echo "ENVIRONMENT = 'server'" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo "class TestBankAccount(unittest.TestCase):" >> testing_python/tests/test_bank_account.py
!echo " def setUp(self):" >> testing_python/tests/test_bank_account.py
!echo " self.account = BankAccount(balance=1000, log_file='test_log.txt')" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def tearDown(self):" >> testing_python/tests/test_bank_account.py
!echo " if os.path.exists('test_log.txt'):" >> testing_python/tests/test_bank_account.py
!echo " os.remove('test_log.txt')" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_deposit(self):" >> testing_python/tests/test_bank_account.py
!echo " new_balace = self.account.deposit(500)" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(new_balace, 1500)" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_withdraw(self):" >> testing_python/tests/test_bank_account.py
!echo " new_balace = self.account.withdraw(200)" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(new_balace, 800, 'Balance is not correct')" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_get_balance(self):" >> testing_python/tests/test_bank_account.py
!echo " balance = self.account.get_balance()" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(balance, 1000)" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_transaction_log(self):" >> testing_python/tests/test_bank_account.py
!echo " self.account.deposit(500)" >> testing_python/tests/test_bank_account.py
!echo " self.account.withdraw(200)" >> testing_python/tests/test_bank_account.py
!echo " self.account.get_balance()" >> testing_python/tests/test_bank_account.py
!echo " self.assertTrue(os.path.exists('test_log.txt'))" >> testing_python/tests/test_bank_account.py
!echo " with open('test_log.txt', 'r') as file:" >> testing_python/tests/test_bank_account.py
!echo " content = file.readlines()" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(content, [" >> testing_python/tests/test_bank_account.py
!echo " 'Account created\\n', " >> testing_python/tests/test_bank_account.py
!echo " 'Deposit 500, new balance 1500\\n', " >> testing_python/tests/test_bank_account.py
!echo " 'Withdraw 200, new balance 1300\\n', " >> testing_python/tests/test_bank_account.py
!echo " 'Balance check, balance 1300\\n'" >> testing_python/tests/test_bank_account.py
!echo " ])" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " @unittest.skip('Not implemented yet')" >> testing_python/tests/test_bank_account.py
!echo " def test_has_loan(self):" >> testing_python/tests/test_bank_account.py
!echo " self.assertFalse(self.account.has_loan())" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " @unittest.skipIf(SERVER, 'Only for local testing')" >> testing_python/tests/test_bank_account.py
!echo " def test_server(self):" >> testing_python/tests/test_bank_account.py
!echo " self.assertTrue(SERVER)" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " @unittest.expectedFailure" >> testing_python/tests/test_bank_account.py
!echo " def test_fail(self):" >> testing_python/tests/test_bank_account.py
!echo " new_balace = self.account.withdraw(1200)" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(new_balace, 800, 'Not enough money')" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " @unittest.skipUnless(ENVIRONMENT == 'dev', 'Only for dev environment')" >> testing_python/tests/test_bank_account.py
!echo " def test_environment(self):" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(ENVIRONMENT, 'dev')" >> testing_python/tests/test_bank_account.py
Copy

Passamos os testes

	
!cd testing_python && python -m unittest discover -s tests -v
Copy
	
test_deposit (test_bank_account.TestBankAccount) ... ok
test_environment (test_bank_account.TestBankAccount) ... skipped 'Only for dev environment'
test_fail (test_bank_account.TestBankAccount) ... expected failure
test_get_balance (test_bank_account.TestBankAccount) ... ok
test_has_loan (test_bank_account.TestBankAccount) ... skipped 'Not implemented yet'
test_server (test_bank_account.TestBankAccount) ... skipped 'Only for local testing'
test_transaction_log (test_bank_account.TestBankAccount) ... ok
test_withdraw (test_bank_account.TestBankAccount) ... ok
test_divide (test_calculator.TestCalculator) ... ok
test_multiply (test_calculator.TestCalculator) ... ok
test_substract (test_calculator.TestCalculator) ... ok
test_sum (test_calculator.TestCalculator) ... ok
----------------------------------------------------------------------
Ran 12 tests in 0.001s
OK (skipped=3, expected failures=1)

Obtemos test_environment (test_bank_account.TestBankAccount) ... skipped 'Only for dev environment' porque esse teste só será executado no ambiente de desenvolvimento e assume-se que estamos no servidor.

Organizar os testeslink image 50

Podemos organizar os testes organizando o que são chamados de test suites. Para isso, vamos criar um arquivo chamado test_suites.py na pasta tests.

	
!cd testing_python && cd tests && touch test_suites.py
Copy

E agora geramos duas suites de teste com os testes de test_calculator e test_bank_account

	
!echo "import unittest" > testing_python/tests/test_suite_calculator.py
!echo "" >> testing_python/tests/test_suite_calculator.py
!echo "from test_calculator import TestCalculator" >> testing_python/tests/test_suite_calculator.py
!echo "" >> testing_python/tests/test_suite_calculator.py
!echo "def calculator_suite():" >> testing_python/tests/test_suite_calculator.py
!echo " suite = unittest.TestSuite()" >> testing_python/tests/test_suite_calculator.py
!echo " suite.addTest(TestCalculator('test_sum'))" >> testing_python/tests/test_suite_calculator.py
!echo " suite.addTest(TestCalculator('test_substract'))" >> testing_python/tests/test_suite_calculator.py
!echo " suite.addTest(TestCalculator('test_multiply'))" >> testing_python/tests/test_suite_calculator.py
!echo " suite.addTest(TestCalculator('test_divide'))" >> testing_python/tests/test_suite_calculator.py
!echo " return suite" >> testing_python/tests/test_suite_calculator.py
!echo "" >> testing_python/tests/test_suite_calculator.py
!echo "if __name__ == '__main__':" >> testing_python/tests/test_suite_calculator.py
!echo " runner = unittest.TextTestRunner()" >> testing_python/tests/test_suite_calculator.py
!echo " runner.run(calculator_suite())" >> testing_python/tests/test_suite_calculator.py
Copy

Agora o de test_bank_account

	
!echo "import unittest" > testing_python/tests/test_suite_bank_account.py
!echo "" >> testing_python/tests/test_suite_bank_account.py
!echo "from test_bank_account import TestBankAccount" >> testing_python/tests/test_suite_bank_account.py
!echo "" >> testing_python/tests/test_suite_bank_account.py
!echo "def bank_account_suite():" >> testing_python/tests/test_suite_bank_account.py
!echo " suite = unittest.TestSuite()" >> testing_python/tests/test_suite_bank_account.py
!echo " suite.addTest(TestBankAccount('test_deposit'))" >> testing_python/tests/test_suite_bank_account.py
!echo " suite.addTest(TestBankAccount('test_withdraw'))" >> testing_python/tests/test_suite_bank_account.py
!echo " suite.addTest(TestBankAccount('test_get_balance'))" >> testing_python/tests/test_suite_bank_account.py
!echo " suite.addTest(TestBankAccount('test_transaction_log'))" >> testing_python/tests/test_suite_bank_account.py
!echo " suite.addTest(TestBankAccount('test_has_loan'))" >> testing_python/tests/test_suite_bank_account.py
!echo " suite.addTest(TestBankAccount('test_server'))" >> testing_python/tests/test_suite_bank_account.py
!echo " suite.addTest(TestBankAccount('test_fail'))" >> testing_python/tests/test_suite_bank_account.py
!echo " suite.addTest(TestBankAccount('test_environment'))" >> testing_python/tests/test_suite_bank_account.py
!echo " return suite" >> testing_python/tests/test_suite_bank_account.py
!echo "" >> testing_python/tests/test_suite_bank_account.py
!echo "if __name__ == '__main__':" >> testing_python/tests/test_suite_bank_account.py
!echo " runner = unittest.TextTestRunner()" >> testing_python/tests/test_suite_bank_account.py
!echo " runner.run(bank_account_suite())" >> testing_python/tests/test_suite_bank_account.py
Copy

Podemos executar apenas os testes de test_calculator com o comando python tests/test_suite_calculator.py

	
!cd testing_python && python tests/test_suite_calculator.py
Copy
	
Traceback (most recent call last):
File "/home/wallabot/Documentos/web/portafolio/posts/testing_python/tests/test_suite_calculator.py", line 3, in <module>
from test_calculator import TestCalculator
File "/home/wallabot/Documentos/web/portafolio/posts/testing_python/tests/test_calculator.py", line 2, in <module>
from src.calculator import sum, substract, multiply, divide
ModuleNotFoundError: No module named 'src'

Vemos que não encontra o módulo src de test_calculator.py, isso é porque ele não está no caminho, então vamos adicioná-lo.

	
!cd testing_python && PYTHONPATH=. python tests/test_suite_calculator.py
Copy
	
....
----------------------------------------------------------------------
Ran 4 tests in 0.000s
OK

Vemos que só passou os testes de test_calculator

Vamos passar agora os de test_bank_account.py

	
!cd testing_python && PYTHONPATH=. python tests/test_suite_bank_account.py
Copy
	
....ssxs
----------------------------------------------------------------------
Ran 8 tests in 0.001s
OK (skipped=3, expected failures=1)

E aqui vemos que passa os testes de test_bank_account

É preciso diferenciar do que fazíamos no início, antes de usar discover, quando executávamos !cd testing_python && python -m unittest tests/test_calculator.py. Porque em test_calculator.py podemos escrever todos os possíveis testes para calculator.py, mas com as suites executamos os testes que quisermos. Podemos ter várias suites para executar os testes que quisermos de test_calculator.py e test_bank_account.py.

Melhores práticas ao nomear os testeslink image 51

Na hora de nomear os testes, é bom seguir as seguintes diretrizes:

  • Uma classe de teste para cada classe de código
  • Todos os testes devem começar com test_ para saber que é um código
  • A seguir deve estar o nome da função ou método que está sendo testado
  • A seguir deve vir o cenário do teste, pois um método ou função pode ter vários cenários de teste. Por exemplo, com todos os possíveis valores de entrada, com valores limite, com valores incorretos, etc.
  • Por último, deve-se adicionar o resultado esperado

Portanto, um teste deve ter o seguinte formato test_<nome_função>_<cenário>_<resultado_esperado>

Mocking de APIslink image 52

Suponhamos que nosso código chama uma API externa e queremos fazer testes do nosso código sem depender da API externa. Para isso, podemos usar unittest.mock, que vem por padrão no Python.

Primeiro vamos a criar um arquivo chamado api.py na pasta src

	
!echo "import requests" > testing_python/src/api.py
!echo "" >> testing_python/src/api.py
!echo "def get_api():" >> testing_python/src/api.py
!echo " url = 'https://jsonplaceholder.typicode.com/posts/1'" >> testing_python/src/api.py
!echo " try:" >> testing_python/src/api.py
!echo " response = requests.get(url)" >> testing_python/src/api.py
!echo " response.raise_for_status()" >> testing_python/src/api.py
!echo " data = response.json()" >> testing_python/src/api.py
!echo " print(data)" >> testing_python/src/api.py
!echo " return data" >> testing_python/src/api.py
!echo " except requests.exceptions.RequestException as e:" >> testing_python/src/api.py
!echo " print(f'error: {e}')" >> testing_python/src/api.py
!echo " return None" >> testing_python/src/api.py
!echo "" >> testing_python/src/api.py
!echo "if __name__ == '__main__':" >> testing_python/src/api.py
!echo " get_api()" >> testing_python/src/api.py
Copy

Vamos a executar o arquivo para ver o que ele retorna.

	
!cd testing_python/src && python api.py
Copy
	
{'userId': 1, 'id': 1, 'title': 'sunt aut facere repellat provident occaecati excepturi optio reprehenderit', 'body': 'quia et suscipit suscipit recusandae consequuntur expedita et cum reprehenderit molestiae ut ut quas totam nostrum rerum est autem sunt rem eveniet architecto'}

Agora criamos o teste

	
!echo "import unittest" > testing_python/tests/test_api.py
!echo "from src.api import get_api" >> testing_python/tests/test_api.py
!echo "" >> testing_python/tests/test_api.py
!echo "class TestApi(unittest.TestCase):" >> testing_python/tests/test_api.py
!echo " def test_get_api(self):" >> testing_python/tests/test_api.py
!echo " data = get_api()" >> testing_python/tests/test_api.py
!echo " self.assertIsNotNone(data)" >> testing_python/tests/test_api.py
!echo " self.assertIsInstance(data, dict)" >> testing_python/tests/test_api.py
!echo " self.assertIn('userId', data)" >> testing_python/tests/test_api.py
!echo " self.assertIn('id', data)" >> testing_python/tests/test_api.py
!echo " self.assertIn('title', data)" >> testing_python/tests/test_api.py
!echo " self.assertIn('body', data)" >> testing_python/tests/test_api.py
!echo " self.assertEqual(data['userId'], 1)" >> testing_python/tests/test_api.py
!echo " self.assertEqual(data['id'], 1)" >> testing_python/tests/test_api.py
Copy

Passamos os testes

	
!cd testing_python && python -m unittest tests/test_api.py
Copy
	
{'userId': 1, 'id': 1, 'title': 'sunt aut facere repellat provident occaecati excepturi optio reprehenderit', 'body': 'quia et suscipit suscipit recusandae consequuntur expedita et cum reprehenderit molestiae ut ut quas totam nostrum rerum est autem sunt rem eveniet architecto'}
.
----------------------------------------------------------------------
Ran 1 test in 0.074s
OK

Vamos a passar os testes de bank_account para ver quanto demora

	
!cd testing_python && PYTHONPATH=. python tests/test_suite_bank_account.py
Copy
	
....ssxs
----------------------------------------------------------------------
Ran 8 tests in 0.001s
OK (skipped=3, expected failures=1)

Como vemos 8 testes demoraram 0.001 segundos, enquanto 1 único teste da API demorou 0.074 segundos. Isso pode fazer com que um código com muitas chamadas a uma API demore muito para executar os testes. Além disso, corremos o risco de que se a API mudar e não retornar o que esperamos, os testes falharão, mesmo que nosso código esteja correto. Então, para isso, as APIs são mockadas.

Vamos a ver como fazer isso.

	
!echo "import unittest" > testing_python/tests/test_api.py
!echo "from src.api import get_api" >> testing_python/tests/test_api.py
!echo "from unittest.mock import patch" >> testing_python/tests/test_api.py
!echo "" >> testing_python/tests/test_api.py
!echo "class TestApi(unittest.TestCase):" >> testing_python/tests/test_api.py
!echo " @patch('src.api.requests.get')" >> testing_python/tests/test_api.py
!echo " def test_get_api(self, mock_get):" >> testing_python/tests/test_api.py
!echo " mock_get.return_value.status_code = 200" >> testing_python/tests/test_api.py
!echo " mock_get.return_value.json.return_value = {" >> testing_python/tests/test_api.py
!echo " 'userId': 1," >> testing_python/tests/test_api.py
!echo " 'id': 1," >> testing_python/tests/test_api.py
!echo " 'title': 'title'," >> testing_python/tests/test_api.py
!echo " 'body': 'body'" >> testing_python/tests/test_api.py
!echo " }" >> testing_python/tests/test_api.py
!echo " data = get_api()" >> testing_python/tests/test_api.py
!echo " self.assertIsNotNone(data)" >> testing_python/tests/test_api.py
!echo " self.assertIsInstance(data, dict)" >> testing_python/tests/test_api.py
!echo " self.assertIn('userId', data)" >> testing_python/tests/test_api.py
!echo " self.assertIn('id', data)" >> testing_python/tests/test_api.py
!echo " self.assertIn('title', data)" >> testing_python/tests/test_api.py
!echo " self.assertIn('body', data)" >> testing_python/tests/test_api.py
!echo " self.assertEqual(data['userId'], 1)" >> testing_python/tests/test_api.py
!echo " self.assertEqual(data['id'], 1)" >> testing_python/tests/test_api.py
!echo " " >> testing_python/tests/test_api.py
!echo " mock_get.assert_called_once_with('https://jsonplaceholder.typicode.com/posts/1')" >> testing_python/tests/test_api.py
Copy

O que fizemos foi

  • Importar patch de unittest.mock.
  • Nós colocamos um decorador na função test_get_data e passamos como parâmetro ao decorador a função que queremos mockar.
  • Criamos um objeto mock_get que vai substituir a função original com o método return_value, que é o valor que a função mockeada vai retornar.
  • Quando get_api() for chamada, em vez de executar a função original, será obtido em data o valor que foi mockeado em return_value
  • Por último, adicionamos a verificação de que é possível chamar a URL da API
	
!cd testing_python && python -m unittest tests/test_api.py
Copy
	
{'userId': 1, 'id': 1, 'title': 'title', 'body': 'body'}
.
----------------------------------------------------------------------
Ran 1 test in 0.001s
OK

Como vemos, agora leva apenas 0,002 segundos.

Modificar o resultado de objetos através do patchlink image 53

Suponhamos que queremos modificar o método de sacar dinheiro para que só possa ser feito em um intervalo de horários.

Primeiro modificamos a função de retirar dinheiro

def sacar(self, amount):
      agora = datetime.now()
      se now.hour < 7 ou now.hour > 18:
      print('Fora do horário comercial')
      retorne None
      if amount > 0:
      self.balance -= amount
      self._log_transaction(f'Saque {amount}, novo saldo {self.balance}')
      return self.balance
      
	
!echo "from datetime import datetime" > testing_python/src/bank_account.py
!echo "" >> testing_python/src/bank_account.py
!echo "class BankAccount:" >> testing_python/src/bank_account.py
!echo " def __init__(self, balance=0, log_file=None):" >> testing_python/src/bank_account.py
!echo " self.balance = balance" >> testing_python/src/bank_account.py
!echo " self.log_file = log_file" >> testing_python/src/bank_account.py
!echo " self._log_transaction('Account created')" >> testing_python/src/bank_account.py
!echo "" >> testing_python/src/bank_account.py
!echo " def _log_transaction(self, message):" >> testing_python/src/bank_account.py
!echo " if self.log_file:" >> testing_python/src/bank_account.py
!echo " with open(self.log_file, 'a') as file:" >> testing_python/src/bank_account.py
!echo " file.write(f'{message}\\ ')" >> testing_python/src/bank_account.py
!echo "" >> testing_python/src/bank_account.py
!echo " def deposit(self, amount):" >> testing_python/src/bank_account.py
!echo " if amount > 0:" >> testing_python/src/bank_account.py
!echo " self.balance += amount" >> testing_python/src/bank_account.py
!echo " self._log_transaction(f'Deposit {amount}, new balance {self.balance}')" >> testing_python/src/bank_account.py
!echo " return self.balance" >> testing_python/src/bank_account.py
!echo "" >> testing_python/src/bank_account.py
!echo " def withdraw(self, amount):" >> testing_python/src/bank_account.py
!echo " now = datetime.now()" >> testing_python/src/bank_account.py
!echo " if now.hour < 7 or now.hour > 18:" >> testing_python/src/bank_account.py
!echo " print('Out of hours')" >> testing_python/src/bank_account.py
!echo " return None" >> testing_python/src/bank_account.py
!echo " if amount > 0:" >> testing_python/src/bank_account.py
!echo " self.balance -= amount" >> testing_python/src/bank_account.py
!echo " self._log_transaction(f'Withdraw {amount}, new balance {self.balance}')" >> testing_python/src/bank_account.py
!echo " return self.balance" >> testing_python/src/bank_account.py
!echo "" >> testing_python/src/bank_account.py
!echo " def get_balance(self):" >> testing_python/src/bank_account.py
!echo " self._log_transaction(f'Balance check, balance {self.balance}')" >> testing_python/src/bank_account.py
!echo " return self.balance" >> testing_python/src/bank_account.py
Copy

Agora precisamos poder testar o saque de dinheiro em diferentes horários, mas não podemos estar mudando a hora do sistema, nem podemos esperar até que seja uma hora específica para realizar o teste. Para isso, podemos usar patch para modificar o resultado de datetime.now()

@patch('src.bank_account.datetime.now')
      def test_retirada_durante_horas_de_trabalho(self, mock_now):
      mock_now.return_value.hour = 10
      new_balance = self.account.withdraw(200)
      self.assertEqual(new_balance, 800, 'O saldo não está correto')
      
      @patch('src.bank_account.datetime.now')
      def testar_retirada_fora_do_horário_de_trabalho(self, mock_agora):
      mock_now.return_value.hour = 20
      new_balance = self.account.withdraw(200)
      self.assertIsNone(new_balance, 'O saldo não está correto')
      
	
!echo "import unittest" > testing_python/tests/test_bank_account.py
!echo "from unittest.mock import patch" >> testing_python/tests/test_bank_account.py
!echo "import os" >> testing_python/tests/test_bank_account.py
!echo "from src.bank_account import BankAccount" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo "SERVER = True" >> testing_python/tests/test_bank_account.py
!echo "ENVIRONMENT = 'server'" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo "class TestBankAccount(unittest.TestCase):" >> testing_python/tests/test_bank_account.py
!echo " def setUp(self):" >> testing_python/tests/test_bank_account.py
!echo " self.account = BankAccount(balance=1000, log_file='test_log.txt')" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def tearDown(self):" >> testing_python/tests/test_bank_account.py
!echo " if os.path.exists('test_log.txt'):" >> testing_python/tests/test_bank_account.py
!echo " os.remove('test_log.txt')" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_deposit(self):" >> testing_python/tests/test_bank_account.py
!echo " new_balace = self.account.deposit(500)" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(new_balace, 1500)" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_withdraw(self):" >> testing_python/tests/test_bank_account.py
!echo " new_balace = self.account.withdraw(200)" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(new_balace, 800, 'Balance is not correct')" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " @patch('src.bank_account.datetime.now')" >> testing_python/tests/test_bank_account.py
!echo " def test_withdraw_during_working_hours(self, mock_now):" >> testing_python/tests/test_bank_account.py
!echo " mock_now.return_value.hour = 10" >> testing_python/tests/test_bank_account.py
!echo " new_balace = self.account.withdraw(200)" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(new_balace, 800, 'Balance is not correct')" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " @patch('src.bank_account.datetime.now')" >> testing_python/tests/test_bank_account.py
!echo " def test_withdraw_during_non_working_hours(self, mock_now):" >> testing_python/tests/test_bank_account.py
!echo " mock_now.return_value.hour = 20" >> testing_python/tests/test_bank_account.py
!echo " new_balace = self.account.withdraw(200)" >> testing_python/tests/test_bank_account.py
!echo " self.assertIsNone(new_balace, 'Balance is not correct')" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_get_balance(self):" >> testing_python/tests/test_bank_account.py
!echo " balance = self.account.get_balance()" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(balance, 1000)" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_transaction_log(self):" >> testing_python/tests/test_bank_account.py
!echo " self.account.deposit(500)" >> testing_python/tests/test_bank_account.py
!echo " self.account.withdraw(200)" >> testing_python/tests/test_bank_account.py
!echo " self.account.get_balance()" >> testing_python/tests/test_bank_account.py
!echo " self.assertTrue(os.path.exists('test_log.txt'))" >> testing_python/tests/test_bank_account.py
!echo " with open('test_log.txt', 'r') as file:" >> testing_python/tests/test_bank_account.py
!echo " content = file.readlines()" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(content, [" >> testing_python/tests/test_bank_account.py
!echo " 'Account created\\n', " >> testing_python/tests/test_bank_account.py
!echo " 'Deposit 500, new balance 1500\\n', " >> testing_python/tests/test_bank_account.py
!echo " 'Withdraw 200, new balance 1300\\n', " >> testing_python/tests/test_bank_account.py
!echo " 'Balance check, balance 1300\\n'" >> testing_python/tests/test_bank_account.py
!echo " ])" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " @unittest.skip('Not implemented yet')" >> testing_python/tests/test_bank_account.py
!echo " def test_has_loan(self):" >> testing_python/tests/test_bank_account.py
!echo " self.assertFalse(self.account.has_loan())" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " @unittest.skipIf(SERVER, 'Only for local testing')" >> testing_python/tests/test_bank_account.py
!echo " def test_server(self):" >> testing_python/tests/test_bank_account.py
!echo " self.assertTrue(SERVER)" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " @unittest.expectedFailure" >> testing_python/tests/test_bank_account.py
!echo " def test_fail(self):" >> testing_python/tests/test_bank_account.py
!echo " new_balace = self.account.withdraw(1200)" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(new_balace, 800, 'Not enough money')" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " @unittest.skipUnless(ENVIRONMENT == 'dev', 'Only for dev environment')" >> testing_python/tests/test_bank_account.py
!echo " def test_environment(self):" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(ENVIRONMENT, 'dev')" >> testing_python/tests/test_bank_account.py
Copy

Passamos os testes

	
!cd testing_python && PYTHONPATH=. python tests/test_suite_bank_account.py
Copy
	
....ssxs
----------------------------------------------------------------------
Ran 8 tests in 0.001s
OK (skipped=3, expected failures=1)

Parametrizar testes com subTestlink image 54

Suponhamos que queremos fazer várias testes de sacar dinheiro para diferentes valores, poderíamos escrever um teste para cada valor de dinheiro que queremos testar, mas seria repetir código. Para isso podemos usar subTest que nos permite fazer várias testes com um único teste.

O que fazemos é criar um dicionário com os diferentes valores que queremos testar e o resultado esperado, e depois com um loop for e subTest realizamos os testes. Assim ficaria o teste do método withdraw

def test_withdraw(self):
      ```markdown
      test_cases = {
      

test_200: { 'amount': 200, 'esperado': 800 }, test_400: { 'amount': 400, 'esperado': 600 }, test_600: { 'amount': 600, 'esperado': 400 }, test_800: { 'amount': 800, 'esperado': 200 }, test_1000: { 'amount': 1000, 'esperado': 0 } }

para nome_do_teste, caso_de_teste in casos_de_teste.items(): com self.subTest(test_name): self.account = BankAccount(balance=1000, log_file='test_log.txt') new_balance = self.account.withdraw(test_case['amount']) self.assertEqual(new_balance, test_case['expected'], 'O saldo não está correto')

	
!echo "import unittest" > testing_python/tests/test_bank_account.py
!echo "from unittest.mock import patch" >> testing_python/tests/test_bank_account.py
!echo "import os" >> testing_python/tests/test_bank_account.py
!echo "from src.bank_account import BankAccount" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo "SERVER = True" >> testing_python/tests/test_bank_account.py
!echo "ENVIRONMENT = 'server'" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo "class TestBankAccount(unittest.TestCase):" >> testing_python/tests/test_bank_account.py
!echo " def setUp(self):" >> testing_python/tests/test_bank_account.py
!echo " self.account = BankAccount(balance=1000, log_file='test_log.txt')" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def tearDown(self):" >> testing_python/tests/test_bank_account.py
!echo " if os.path.exists('test_log.txt'):" >> testing_python/tests/test_bank_account.py
!echo " os.remove('test_log.txt')" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_deposit(self):" >> testing_python/tests/test_bank_account.py
!echo " new_balace = self.account.deposit(500)" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(new_balace, 1500)" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_withdraw(self):" >> testing_python/tests/test_bank_account.py
!echo " test_cases = {" >> testing_python/tests/test_bank_account.py
!echo " 'test_200': {" >> testing_python/tests/test_bank_account.py
!echo " 'amount': 200," >> testing_python/tests/test_bank_account.py
!echo " 'expected': 800" >> testing_python/tests/test_bank_account.py
!echo " }," >> testing_python/tests/test_bank_account.py
!echo " 'test_400': {" >> testing_python/tests/test_bank_account.py
!echo " 'amount': 400," >> testing_python/tests/test_bank_account.py
!echo " 'expected': 600" >> testing_python/tests/test_bank_account.py
!echo " }," >> testing_python/tests/test_bank_account.py
!echo " 'test_600': {" >> testing_python/tests/test_bank_account.py
!echo " 'amount': 600," >> testing_python/tests/test_bank_account.py
!echo " 'expected': 400" >> testing_python/tests/test_bank_account.py
!echo " }," >> testing_python/tests/test_bank_account.py
!echo " 'test_800': {" >> testing_python/tests/test_bank_account.py
!echo " 'amount': 800," >> testing_python/tests/test_bank_account.py
!echo " 'expected': 200" >> testing_python/tests/test_bank_account.py
!echo " }," >> testing_python/tests/test_bank_account.py
!echo " 'test_1000': {" >> testing_python/tests/test_bank_account.py
!echo " 'amount': 1000," >> testing_python/tests/test_bank_account.py
!echo " 'expected': 0" >> testing_python/tests/test_bank_account.py
!echo " }" >> testing_python/tests/test_bank_account.py
!echo " }" >> testing_python/tests/test_bank_account.py
!echo " for test_case, values in test_cases.items():" >> testing_python/tests/test_bank_account.py
!echo " with self.subTest(test_case=test_case):" >> testing_python/tests/test_bank_account.py
!echo " self.account = BankAccount(balance=1000, log_file='test_log.txt')" >> testing_python/tests/test_bank_account.py
!echo " new_balace = self.account.withdraw(values['amount'])" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(new_balace, values['expected'], 'Balance is not correct')" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " @patch('src.bank_account.datetime.now')" >> testing_python/tests/test_bank_account.py
!echo " def test_withdraw_during_working_hours(self, mock_now):" >> testing_python/tests/test_bank_account.py
!echo " mock_now.return_value.hour = 10" >> testing_python/tests/test_bank_account.py
!echo " new_balace = self.account.withdraw(200)" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(new_balace, 800, 'Balance is not correct')" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " @patch('src.bank_account.datetime.now')" >> testing_python/tests/test_bank_account.py
!echo " def test_withdraw_during_non_working_hours(self, mock_now):" >> testing_python/tests/test_bank_account.py
!echo " mock_now.return_value.hour = 20" >> testing_python/tests/test_bank_account.py
!echo " new_balace = self.account.withdraw(200)" >> testing_python/tests/test_bank_account.py
!echo " self.assertIsNone(new_balace, 'Balance is not correct')" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_get_balance(self):" >> testing_python/tests/test_bank_account.py
!echo " balance = self.account.get_balance()" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(balance, 1000)" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " def test_transaction_log(self):" >> testing_python/tests/test_bank_account.py
!echo " self.account.deposit(500)" >> testing_python/tests/test_bank_account.py
!echo " self.account.withdraw(200)" >> testing_python/tests/test_bank_account.py
!echo " self.account.get_balance()" >> testing_python/tests/test_bank_account.py
!echo " self.assertTrue(os.path.exists('test_log.txt'))" >> testing_python/tests/test_bank_account.py
!echo " with open('test_log.txt', 'r') as file:" >> testing_python/tests/test_bank_account.py
!echo " content = file.readlines()" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(content, [" >> testing_python/tests/test_bank_account.py
!echo " 'Account created\\n', " >> testing_python/tests/test_bank_account.py
!echo " 'Deposit 500, new balance 1500\\n', " >> testing_python/tests/test_bank_account.py
!echo " 'Withdraw 200, new balance 1300\\n', " >> testing_python/tests/test_bank_account.py
!echo " 'Balance check, balance 1300\\n'" >> testing_python/tests/test_bank_account.py
!echo " ])" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " @unittest.skip('Not implemented yet')" >> testing_python/tests/test_bank_account.py
!echo " def test_has_loan(self):" >> testing_python/tests/test_bank_account.py
!echo " self.assertFalse(self.account.has_loan())" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " @unittest.skipIf(SERVER, 'Only for local testing')" >> testing_python/tests/test_bank_account.py
!echo " def test_server(self):" >> testing_python/tests/test_bank_account.py
!echo " self.assertTrue(SERVER)" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " @unittest.expectedFailure" >> testing_python/tests/test_bank_account.py
!echo " def test_fail(self):" >> testing_python/tests/test_bank_account.py
!echo " new_balace = self.account.withdraw(1200)" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(new_balace, 800, 'Not enough money')" >> testing_python/tests/test_bank_account.py
!echo "" >> testing_python/tests/test_bank_account.py
!echo " @unittest.skipUnless(ENVIRONMENT == 'dev', 'Only for dev environment')" >> testing_python/tests/test_bank_account.py
!echo " def test_environment(self):" >> testing_python/tests/test_bank_account.py
!echo " self.assertEqual(ENVIRONMENT, 'dev')" >> testing_python/tests/test_bank_account.py
Copy

Passamos os testes

	
!cd testing_python && PYTHONPATH=. python tests/test_suite_bank_account.py
Copy
	
....ssxs
----------------------------------------------------------------------
Ran 8 tests in 0.001s
OK (skipped=3, expected failures=1)

Cobertura de testelink image 55

Podemos ver a cobertura dos testes que temos, pois mesmo pensando que testamos tudo, pode haver partes do código que não foram testadas. Para isso, vamos usar a biblioteca coverage

	
!cd testing_python && coverage run -m unittest tests/test_calculator.py
Copy
	
....
----------------------------------------------------------------------
Ran 4 tests in 0.000s
OK

Como podemos ver, apenas passamos os testes de test_calculator, mas ainda não sabemos a cobertura.

Relatório de coberturalink image 56

Depois de executar coverage, podemos solicitar um relatório da cobertura dos testes com o comando coverage report.

	
!cd testing_python && coverage report
Copy
	
Name Stmts Miss Cover
----------------------------------------------
src/calculator.py 8 0 100%
tests/test_calculator.py 11 0 100%
----------------------------------------------
TOTAL 19 0 100%

O pedi-lo por meio de um arquivo HTML com o comando coverage html

	
!cd testing_python && coverage html
Copy
	
Wrote HTML report to ]8;;file:///home/wallabot/Documentos/web/portafolio/posts/testing_python/htmlcov/index.htmlhtmlcov/index.html]8;;

Cria-se um arquivo index.html na pasta htmlcov que podemos abrir no navegador e ver a cobertura dos testes.

Continuar lendo

Últimos posts -->

Você viu esses projetos?

Horeca chatbot

Horeca chatbot Horeca chatbot
Python
LangChain
PostgreSQL
PGVector
React
Kubernetes
Docker
GitHub Actions

Chatbot conversacional para cozinheiros de hotéis e restaurantes. Um cozinheiro, gerente de cozinha ou serviço de quarto de um hotel ou restaurante pode falar com o chatbot para obter informações sobre receitas e menus. Mas também implementa agentes, com os quais pode editar ou criar novas receitas ou menus

Naviground

Naviground Naviground

Subtify

Subtify Subtify
Python
Whisper
Spaces

Gerador de legendas para vídeos no idioma que você desejar. Além disso, coloca uma legenda de cor diferente para cada pessoa

Ver todos os projetos -->

Quer aplicar IA no seu projeto? Entre em contato!

Quer melhorar com essas dicas?

Últimos tips -->

Use isso localmente

Os espaços do Hugging Face nos permitem executar modelos com demos muito simples, mas e se a demo quebrar? Ou se o usuário a deletar? Por isso, criei contêineres docker com alguns espaços interessantes, para poder usá-los localmente, aconteça o que acontecer. Na verdade, se você clicar em qualquer botão de visualização de projeto, ele pode levá-lo a um espaço que não funciona.

Flow edit

Flow edit Flow edit

Edite imagens com este modelo de Flow. Baseado em SD3 ou FLUX, você pode editar qualquer imagem e gerar novas

FLUX.1-RealismLora

FLUX.1-RealismLora FLUX.1-RealismLora
Ver todos os contêineres -->

Quer aplicar IA no seu projeto? Entre em contato!

Você quer treinar seu modelo com esses datasets?

short-jokes-dataset

Dataset com piadas em inglês

opus100

Dataset com traduções de inglês para espanhol

netflix_titles

Dataset com filmes e séries da Netflix

Ver mais datasets -->