unittest

unittest unittest

unittest Pythonlink image 19

Disclaimer: This post has been translated to English using a machine translation model. Please, let me know if you find any mistakes.

To create this post, we are going to create a folder called testing_python where we will create all the code.

	
!mkdir testing_python
Copy

Inside that folder we are going to create the src and tests folders where we will put the source code and the respective tests.

	
!mkdir testing_python/src
!mkdir testing_python/tests
Copy

Necessary Librarieslink image 20

To run the tests we are going to use the unittest library that comes by default with Python, but we will also install coverage to see the test coverage. We install it with Conda

conda install conda-forge::coverage```
      
      Or with pip
      ```bash
      pip install coverage```
      

First testslink image 21

Let's create a first file called calculator.py in the src folder

	
!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

Now we create a file called test_calculator.py in the tests folder

	
!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

Now to run it we do 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

As we can see, four dots appear for the two tests that have been passed and were correct.

Let's modify the test file to cause an error, we're going to make it so that adding 2 and 2 gives us 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

Now we run the tests

	
!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)

As we can see, now we get an F which means that a test has failed, in addition, it provides us with the following information

FAIL: test_sum (tests.test_calculator.TestCalculator)----------------------------------------------------------------------Traceback (most recent call last):File "/home/wallabot/Documents/web/portfolio/posts/testing_python/tests/test_calculator.py", line 6, in test_sumself.assertEqual(sum(2, 2), 5)AssertionError: 4 != 5```
      
      It's telling us that the test `test_sum` has failed on line 6, which is the one we modified, and that the expected result was 5 but the obtained result was 4.

One thing we haven't mentioned and is important is that we didn't call the methods test_sum and test_subtract directly; they were executed automatically. This is because methods that start with test_ are the ones that get executed automatically.

A simpler way to run the tests is to use the discover command, which looks for all files starting with test_ in the folder passed through the -s parameter.

First, we rewrite the tests properly.

	
!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

And now we run the tests using discover

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

He found the tests and passed them successfully

Setting up the testslink image 22

setUplink image 23

With the unittest library we can configure the tests, but to see it, first we are going to create a new code file called bank_account.py in the src folder.

	
!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

Now we add tests to the new code, creating a file called test_bank_account.py in the tests folder.

Let's create the test for adding a deposit first, that is, the deposit method.

	
!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

We pass the tests

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

We see that there are five points, but we have only written one test, so we use the flag -v to get more information.

	
!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

We see that discover has found the tests test_calculator and test_bank_account and both have passed.

Let's create the tests for the rest of the methods

	
!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

We pass the tests

	
!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

We see that they have passed successfully. Now let's look at one thing: in all the tests we did account = BankAccount(balance=1000) and then called the methods. This is because each test runs on a new object, meaning objects are not shared between tests. So we can use the setUp method to create an object that is shared among all tests.

	
!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

As we can see, we have created the account in the setUp method and removed the account creation in the tests. Let's run the tests.

	
!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 24

Just like with the setUp method we configure the environment before running the tests, with the tearDown method we can clean up the environment after running the tests. To test this, let's add to the code of bank_account.py that operations are written to a log file.

	
!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

Now we add a test to the new method _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

We pass the tests

	
!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)

The tests have gone well, but we see that in the log file there are many lines with the text Account created, this is because at the beginning of each test the setUp method is executed which creates an account, so we need to create the tearDown method to delete the log file after each test.

Since this is a file generated for the test, it should not exist after running the tests, so let's add the tearDown method to delete the file.

	
!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

We run the tests again

	
!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)

But now we get an error, because since at the end of each test we have deleted the log file, we don't need to check that so much text has been written, but only the text from the test we are running. So let's modify the test to only check that the text from the test has been written.

	
!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

And we pass the tests

	
!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

Error Documentationlink image 25

If you've noticed, so far in the tests we have been using assertEqual. This method gives us the option to write an error message when the condition is not met. Let's modify a test to make it fail and see the error message.

	
!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

We pass the tests

	
!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)

As we can see, we have the message we wrote in the test AssertionError: 800 != 500 : Balance is not correct

assertslink image 26

So far we have used assertEqual, but there are more, such as assertTrue. Let's modify the line assert os.path.exists('test_log.txt') to 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

And we pass the tests

	
!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

Some other asserts we can use are:* assertEqual* assertNotEqual* assertTrue* assertFalse* assertRaises

skiplink image 27

Just like in Python we can use pass to do nothing, in tests we can use skip to skip a test. This can be useful when we know we want to pass some tests, but the code to pass them is not yet ready.

I'm going to add a test that checks if the user has a loan, but since there is no code related to loans yet, I will skip the test.

	
!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

Let's see what happens when we run it

	
!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)

We can see test_has_loan (test_bank_account.TestBankAccount) ... skipped 'Not implemented yet' that test has been skipped as we wanted.

skipIflink image 28

Another option is to use skipIf to skip a test if a condition is met. I'm going to add a variable at the beginning called server to know whether we are in a local environment or on the server, and the test will check this variable so that if we are on the server, it will skip the test.

	
!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

If we pass the tests

	
!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)

We see test_server (test_bank_account.TestBankAccount) ... skipped 'Only for local testing' since, as SERVER = True, the test has been skipped.

expectedFailurelink image 29

An error should be produced when a user tries to withdraw more money than they have, so with the expectedFailure method we can state that we expect the test to fail.

	
!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

We pass the tests

	
!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)

As we can see, we get test_fail (test_bank_account.TestBankAccount) ... expected failure because it is an error we expected.

skipUnlesslink image 30

Just like before, there may be tests that we only want to run in the development environment, but never on the server, or in staging, or in production. For this, we can use 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

We pass the tests

	
!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)

We get test_environment (test_bank_account.TestBankAccount) ... skipped 'Only for dev environment' because that test is only going to pass in the development environment and we are supposed to be on the server.

Organize the testslink image 31

We can organize the tests by creating what are called test suites. To do this, we will create a file called test_suites.py in the tests folder.

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

And now we generate two test suites with the tests from test_calculator and 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

Now the one for 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

We can run only the test_calculator tests with the command 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'

We see that it cannot find the src module from test_calculator.py, this is because it is not in the path, so let's add it.

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

We see that only the test_calculator tests have passed.

Let's move on to the ones in 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)

And here we see that it passes the test_bank_account tests.

We need to differentiate from what we did at the beginning, before using discover, when we did !cd testing_python && python -m unittest tests/test_calculator.py. Because in test_calculator.py we can write all possible tests for calculator.py, but with suites we run the tests we want. We could have several suites to run the tests we want from test_calculator.py and test_bank_account.py.

Best practices when naming testslink image 32

When naming tests, it's a good idea to follow these guidelines:* One test class for each code class* All tests must start with test_ to know that it is a code* The name of the function or method being tested should follow below* The test scenario should follow, as a method or function can have several test scenarios. For example, with all possible input values, with boundary values, with incorrect values, etc.* Finally, the expected result should be added So a test should have the following format test_<function_name>_<scenario>_<expected_result>

API Mockinglink image 33

Let's assume our code calls an external API and we want to test our code without depending on the external API. For this, we can use unittest.mock, which comes by default in Python.

First, let's create a file called api.py in the src folder

	
!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

Let's run the file to see what it returns

	
!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'}

Now we create the test

	
!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

We pass the tests

	
!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

Let's run the tests for bank_account to see how long it takes

	
!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)

As we can see, 8 tests took 0.001 seconds, while a single API test took 0.074 seconds. This can make a code with many calls to an API take a long time to run the tests. Additionally, there's a risk that if the API changes and doesn't return what we expect, the tests will fail even though our code is correct. That's why APIs are mocked.

Let's see how to do it

	
!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

What we have done is* Import patch from unittest.mock.* We have added a decorator to the function test_get_data and passed as a parameter to the decorator the function we want to mock.* We have created a mock_get object that will replace the original function with the return_value method, which is the value that the mocked function will return.* When get_api() is called, instead of executing the original function, data will contain the value we have mocked in return_value* Finally, we added the check that the URL of the API can be called.

	
!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

As we can see, it now only takes 0.002 seconds.

Modify the result of objects using patchlink image 34

Suppose we want to modify the method of withdrawing money so that it can only be done within a specific range of hours. First we modify the withdraw function

def withdraw(self, amount):now = datetime.now()if now.hour < 7 or now.hour > 18:print('Out of hours')return Noneif amount > 0:self.balance -= amountself._log_transaction(f'Withdraw {amount}, new balance {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

Now we need to be able to test the withdrawal of money at different times, but we can't keep changing the system time, nor can we wait for it to be a specific time to run the test. For this, we can use patch to modify the result of datetime.now()

@patch('src.bank_account.datetime.now')def test_withdraw_during_working_hours(self, mock_now):mock_now.return_value.hour = 10new_balance = self.account.withdraw(200)self.assertEqual(new_balance, 800, 'Balance is not correct')
      @patch('src.bank_account.datetime.now')def test_withdraw_during_non_working_hours(self, mock_now):mock_now.return_value.hour = 20new_balance = self.account.withdraw(200)self.assertIsNone(new_balance, 'Balance is not correct')```
      
	
!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

We pass the tests

	
!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)

Parameterizing tests with subTestlink image 35

Suppose we want to run several tests for withdrawing money with different values. We could write a test for each value of money we want to test, but that would be repetitive code. For this, we can use subTest, which allows us to perform multiple tests with a single test case. What we do is create a dictionary with the different values we want to test and the expected result, and then with a for loop and subTest we perform the tests. This is how the test for the withdraw method would look:

def test_withdraw(self):test_cases = {test_200: {'amount': 200,'expected': 800},test_400: {'amount': 400,'expected': 600},test_600: {'amount': 600,'expected': 400},test_800: {'amount': 800,'expected': 200},test_1000: {'amount': 1000,'expected': 0}It seems like you might have accidentally sent an incomplete request. If you have a Markdown text to translate, please provide it and I'll be happy to help!
      for test_name, test_case in test_cases.items():with 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'], 'Balance is not correct')```
      
	
!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

We pass the tests

	
!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)

Test Coveragelink image 36

We can see the test coverage we have, as even though we may think we have tested everything, there might be parts of the code that we haven't tested. For this, we are going to use the coverage library.

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

As we can see, we have only passed the test_calculator tests, but we still don't know the coverage.

Coverage Reportlink image 37

After running coverage, we can request a test coverage report with the command 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%

Or request it through an HTML file with the command 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;;

An index.html file is created in the htmlcov folder that we can open in the browser to view the test coverage.

Continue reading

Last posts -->

Have you seen these projects?

Horeca chatbot

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

Chatbot conversational for cooks of hotels and restaurants. A cook, kitchen manager or room service of a hotel or restaurant can talk to the chatbot to get information about recipes and menus. But it also implements agents, with which it can edit or create new recipes or menus

Subtify

Subtify Subtify
Python
Whisper
Spaces

Subtitle generator for videos in the language you want. Also, it puts a different color subtitle to each person

View all projects -->

Do you want to apply AI in your project? Contact me!

Do you want to improve with these tips?

Last tips -->

Use this locally

Hugging Face spaces allow us to run models with very simple demos, but what if the demo breaks? Or if the user deletes it? That's why I've created docker containers with some interesting spaces, to be able to use them locally, whatever happens. In fact, if you click on any project view button, it may take you to a space that doesn't work.

Flow edit

Flow edit Flow edit

FLUX.1-RealismLora

FLUX.1-RealismLora FLUX.1-RealismLora
View all containers -->

Do you want to apply AI in your project? Contact me!

Do you want to train your model with these datasets?

short-jokes-dataset

Dataset with jokes in English

opus100

Dataset with translations from English to Spanish

netflix_titles

Dataset with Netflix movies and series

View more datasets -->