unittest Python
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
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
Bibliotecas necessárias
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 testes
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
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
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
....----------------------------------------------------------------------Ran 4 tests in 0.000sOK
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
Agora rodamos os testes
!cd testing_python && python -m unittest tests/test_calculator.py
...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_sumself.assertEqual(sum(2, 2), 5)AssertionError: 4 != 5----------------------------------------------------------------------Ran 4 tests in 0.000sFAILED (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
E agora passamos os testes através de discover
!cd testing_python && python -m unittest discover -s tests
....----------------------------------------------------------------------Ran 4 tests in 0.000sOK
Ele encontrou os testes e os passou corretamente
Configuração dos testes
setUp

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
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
Passamos os testes
!cd testing_python && python -m unittest discover -s tests
.....----------------------------------------------------------------------Ran 5 tests in 0.000sOK
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
test_deposit (test_bank_account.TestBankAccount) ... oktest_divide (test_calculator.TestCalculator) ... oktest_multiply (test_calculator.TestCalculator) ... oktest_substract (test_calculator.TestCalculator) ... oktest_sum (test_calculator.TestCalculator) ... ok----------------------------------------------------------------------Ran 5 tests in 0.000sOK
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
Passamos os testes
!cd testing_python && python -m unittest discover -s tests -v
test_deposit (test_bank_account.TestBankAccount) ... oktest_get_balance (test_bank_account.TestBankAccount) ... oktest_withdraw (test_bank_account.TestBankAccount) ... oktest_divide (test_calculator.TestCalculator) ... oktest_multiply (test_calculator.TestCalculator) ... oktest_substract (test_calculator.TestCalculator) ... oktest_sum (test_calculator.TestCalculator) ... ok----------------------------------------------------------------------Ran 7 tests in 0.000sOK
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
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
test_deposit (test_bank_account.TestBankAccount) ... oktest_get_balance (test_bank_account.TestBankAccount) ... oktest_withdraw (test_bank_account.TestBankAccount) ... oktest_divide (test_calculator.TestCalculator) ... oktest_multiply (test_calculator.TestCalculator) ... oktest_substract (test_calculator.TestCalculator) ... oktest_sum (test_calculator.TestCalculator) ... ok----------------------------------------------------------------------Ran 7 tests in 0.000sOK
tearDown

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
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
Passamos os testes
!cd testing_python && python -m unittest discover -s tests -v
test_deposit (test_bank_account.TestBankAccount) ... oktest_get_balance (test_bank_account.TestBankAccount) ... oktest_transaction_log (test_bank_account.TestBankAccount) ... FAILtest_withdraw (test_bank_account.TestBankAccount) ... oktest_divide (test_calculator.TestCalculator) ... oktest_multiply (test_calculator.TestCalculator) ... oktest_substract (test_calculator.TestCalculator) ... oktest_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_logself.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.001sFAILED (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
Voltamos a passar os testes
!cd testing_python && python -m unittest discover -s tests -v
test_deposit (test_bank_account.TestBankAccount) ... oktest_get_balance (test_bank_account.TestBankAccount) ... oktest_transaction_log (test_bank_account.TestBankAccount) ... FAILtest_withdraw (test_bank_account.TestBankAccount) ... oktest_divide (test_calculator.TestCalculator) ... oktest_multiply (test_calculator.TestCalculator) ... oktest_substract (test_calculator.TestCalculator) ... oktest_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_logself.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.001sFAILED (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
E passamos os testes
!cd testing_python && python -m unittest discover -s tests -v
test_deposit (test_bank_account.TestBankAccount) ... oktest_get_balance (test_bank_account.TestBankAccount) ... oktest_transaction_log (test_bank_account.TestBankAccount) ... oktest_withdraw (test_bank_account.TestBankAccount) ... oktest_divide (test_calculator.TestCalculator) ... oktest_multiply (test_calculator.TestCalculator) ... oktest_substract (test_calculator.TestCalculator) ... oktest_sum (test_calculator.TestCalculator) ... ok----------------------------------------------------------------------Ran 8 tests in 0.001sOK
Documentação de erros
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
Passamos os testes
!cd testing_python && python -m unittest discover -s tests -v
test_deposit (test_bank_account.TestBankAccount) ... oktest_get_balance (test_bank_account.TestBankAccount) ... oktest_transaction_log (test_bank_account.TestBankAccount) ... oktest_withdraw (test_bank_account.TestBankAccount) ... FAILtest_divide (test_calculator.TestCalculator) ... oktest_multiply (test_calculator.TestCalculator) ... oktest_substract (test_calculator.TestCalculator) ... oktest_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_withdrawself.assertEqual(new_balace, 500, 'Balance is not correct')AssertionError: 800 != 500 : Balance is not correct----------------------------------------------------------------------Ran 8 tests in 0.001sFAILED (failures=1)
Como podemos ver, temos a mensagem que escrevemos no teste AssertionError: 800 != 500 : Balance is not correct
assert
s
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
E passamos os testes
!cd testing_python && python -m unittest discover -s tests -v
test_deposit (test_bank_account.TestBankAccount) ... oktest_get_balance (test_bank_account.TestBankAccount) ... oktest_transaction_log (test_bank_account.TestBankAccount) ... oktest_withdraw (test_bank_account.TestBankAccount) ... oktest_divide (test_calculator.TestCalculator) ... oktest_multiply (test_calculator.TestCalculator) ... oktest_substract (test_calculator.TestCalculator) ... oktest_sum (test_calculator.TestCalculator) ... ok----------------------------------------------------------------------Ran 8 tests in 0.001sOK
Alguns outros assert
s que podemos usar são:
assertEqual
assertNotEqual
assertTrue
assertFalse
assertRaises
skip

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
Vamos ver o que acontece ao executá-lo
!cd testing_python && python -m unittest discover -s tests -v
test_deposit (test_bank_account.TestBankAccount) ... oktest_get_balance (test_bank_account.TestBankAccount) ... oktest_has_loan (test_bank_account.TestBankAccount) ... skipped 'Not implemented yet'test_transaction_log (test_bank_account.TestBankAccount) ... oktest_withdraw (test_bank_account.TestBankAccount) ... oktest_divide (test_calculator.TestCalculator) ... oktest_multiply (test_calculator.TestCalculator) ... oktest_substract (test_calculator.TestCalculator) ... oktest_sum (test_calculator.TestCalculator) ... ok----------------------------------------------------------------------Ran 9 tests in 0.001sOK (skipped=1)
Podemos ver test_has_loan (test_bank_account.TestBankAccount) ... skipped 'Not implemented yet'
que esse teste foi pulado como queríamos.
skipIf

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
Se passarmos nos testes
!cd testing_python && python -m unittest discover -s tests -v
test_deposit (test_bank_account.TestBankAccount) ... oktest_get_balance (test_bank_account.TestBankAccount) ... oktest_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) ... oktest_withdraw (test_bank_account.TestBankAccount) ... oktest_divide (test_calculator.TestCalculator) ... oktest_multiply (test_calculator.TestCalculator) ... oktest_substract (test_calculator.TestCalculator) ... oktest_sum (test_calculator.TestCalculator) ... ok----------------------------------------------------------------------Ran 10 tests in 0.001sOK (skipped=2)
Vemos test_server (test_bank_account.TestBankAccount) ... skipped 'Only for local testing'
já que, como SERVER = True
, o teste foi pulado.
falhaEsperada

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
Passamos os testes
!cd testing_python && python -m unittest discover -s tests -v
test_deposit (test_bank_account.TestBankAccount) ... oktest_fail (test_bank_account.TestBankAccount) ... expected failuretest_get_balance (test_bank_account.TestBankAccount) ... oktest_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) ... oktest_withdraw (test_bank_account.TestBankAccount) ... oktest_divide (test_calculator.TestCalculator) ... oktest_multiply (test_calculator.TestCalculator) ... oktest_substract (test_calculator.TestCalculator) ... oktest_sum (test_calculator.TestCalculator) ... ok----------------------------------------------------------------------Ran 11 tests in 0.001sOK (skipped=2, expected failures=1)
Como vemos, obtemos test_fail (test_bank_account.TestBankAccount) ... expected failure
porque é um erro que esperávamos
skipUnless

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
Passamos os testes
!cd testing_python && python -m unittest discover -s tests -v
test_deposit (test_bank_account.TestBankAccount) ... oktest_environment (test_bank_account.TestBankAccount) ... skipped 'Only for dev environment'test_fail (test_bank_account.TestBankAccount) ... expected failuretest_get_balance (test_bank_account.TestBankAccount) ... oktest_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) ... oktest_withdraw (test_bank_account.TestBankAccount) ... oktest_divide (test_calculator.TestCalculator) ... oktest_multiply (test_calculator.TestCalculator) ... oktest_substract (test_calculator.TestCalculator) ... oktest_sum (test_calculator.TestCalculator) ... ok----------------------------------------------------------------------Ran 12 tests in 0.001sOK (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 testes
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
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
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
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
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 TestCalculatorFile "/home/wallabot/Documentos/web/portafolio/posts/testing_python/tests/test_calculator.py", line 2, in <module>from src.calculator import sum, substract, multiply, divideModuleNotFoundError: 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
....----------------------------------------------------------------------Ran 4 tests in 0.000sOK
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
....ssxs----------------------------------------------------------------------Ran 8 tests in 0.001sOK (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 suite
s executamos os testes que quisermos. Podemos ter várias suite
s para executar os testes que quisermos de test_calculator.py
e test_bank_account.py
.
Melhores práticas ao nomear os testes
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 APIs
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
Vamos a executar o arquivo para ver o que ele retorna.
!cd testing_python/src && python api.py
{'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
Passamos os testes
!cd testing_python && python -m unittest tests/test_api.py
{'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.074sOK
Vamos a passar os testes de bank_account
para ver quanto demora
!cd testing_python && PYTHONPATH=. python tests/test_suite_bank_account.py
....ssxs----------------------------------------------------------------------Ran 8 tests in 0.001sOK (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
O que fizemos foi
- Importar
patch
deunittest.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étodoreturn_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 emdata
o valor que foi mockeado emreturn_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
{'userId': 1, 'id': 1, 'title': 'title', 'body': 'body'}.----------------------------------------------------------------------Ran 1 test in 0.001sOK
Como vemos, agora leva apenas 0,002 segundos.
Modificar o resultado de objetos através do patch

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
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
Passamos os testes
!cd testing_python && PYTHONPATH=. python tests/test_suite_bank_account.py
....ssxs----------------------------------------------------------------------Ran 8 tests in 0.001sOK (skipped=3, expected failures=1)
Parametrizar testes com subTest

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
Passamos os testes
!cd testing_python && PYTHONPATH=. python tests/test_suite_bank_account.py
....ssxs----------------------------------------------------------------------Ran 8 tests in 0.001sOK (skipped=3, expected failures=1)
Cobertura de teste
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
....----------------------------------------------------------------------Ran 4 tests in 0.000sOK
Como podemos ver, apenas passamos os testes de test_calculator
, mas ainda não sabemos a cobertura.
Relatório de cobertura
Depois de executar coverage
, podemos solicitar um relatório da cobertura dos testes com o comando coverage report
.
!cd testing_python && coverage report
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
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.