Python Unit Tests: Quick Start

A translation of the article was prepared specifically for students of the Python QA Engineer course.

Unit code testing is an integral part of the software development life cycle. Unit tests also form the basis for conducting regression testing, that is, they guarantee that the system will behave according to the scenario when new features are added or existing ones change.

In this article, I will demonstrate the basic idea of ​​unit testing in one class. In practice, you will have to write many test cases, add them to the test suite, and run them all together. Test case management will be discussed in the next article.

Today we will focus on testing the backend. That is, the developer has implemented some project according to the specifications (for example, Calculator.py), and your task is to make sure that the developed code really matches them (for example, using `TestCalculator.py`)

Suppose you wrote the Calculator class to perform basic computational functions: addition, subtraction, multiplication, and division.

The code for this is here (`Calculator.py`):

``````#A simple calculator
class Calculator:
#empty constructor
def __init __ (self):
pass
#add method - given two numbers, return the addition
def add (self, x1, x2):
return x1 + x2
#multiply method - given two numbers, return the
#multiplication of the two
def multiply (self, x1, x2):
return x1 * x2
#subtract method - given two numbers, return the value
#of first value minus the second
def subtract (self, x1, x2):
return x1 - x2
#divide method - given two numbers, return the value
#of first value divided by the second
def divide (self, x1, x2):
if x2! = 0:
return x1 / x2``````

Now I want to run a unit test to understand that the functionality in the above class works as planned.

Python usually comes with a package. `unittest`. If it is not on your system, use pip to install it.

The unit test has the following structure:

`setUp ()` and `tearDown ()` These are standard methods that come with the unittest framework (they are defined in the unittest.TestCase class). Depending on your test case, you may or may not overwrite these two methods by default.

It's time to look at the code of the test case. Here is the file `TestCalculator.py`.

``````import unittest
from Calculator import Calculator
#Test cases to test Calulator methods
#You always create a child class derived from unittest.TestCase
class TestCalculator (unittest.TestCase):
#setUp method is overridden from the parent class TestCase
def setUp (self):
self.calculator = Calculator ()
#Each test method starts with the keyword test_
self.assertEqual (self.calculator.add (4.7), 11)
def test_subtract (self):
self.assertEqual (self.calculator.subtract (10,5), 5)
def test_multiply (self):
self.assertEqual (self.calculator.multiply (3,7), 21)
def test_divide (self):
self.assertEqual (self.calculator.divide (10,2), 5)
# Executing the tests in the above test case class
if __name__ == "__main__":
unittest.main ()``````

Although this is not necessary, but as a rule I call the test class with the prefix Test (in our case TestCalculator). The key requirement in this class is to have a superclass `unittest.TestCase`.

Whenever this test case is executed, the setUp () method is executed first. In our case, we simply create an object of the class Calculator and save it as an attribute of the class. There are several other default methods in the parent class, which we will discuss later.

For now, all you will do is write methods `test_xxx` to test each method in the Calculator class. Please note that all test methods start with a prefix. `test_`. This tells Python using the unittest framework that these are test methods.

In each of the testing methods, I used the built-in method `assertEqual`to check if the calculator methods return the expected value. If the return value is equal to the expected value, the test succeeds; otherwise, it fails.

There are many built-in methods. `assert`which we will talk about later.

The last line in the code above just starts the test case `Testcalculator`. It executes each test method defined inside the class and returns the result.

``python TestCalculator.py -v``

You will see a conclusion similar to the following:

``````test_add (__main __. TestCalculator) ... ok
test_divide (__main __. TestCalculator) ... ok
test_multiply (__main __. TestCalculator) ... ok
test_subtract (__main __. TestCalculator) ... ok

-------------------------------------------------- ------------------
Ran 4 tests in 0.000s

Ok``````

What if something doesn't work as expected? Let's change the expected value `test_divide` from 5 to 6 (5 is the correct value, now we will see what happens if it fails. This is not an error in the source code, but an error in the test suite, you may also have errors in the test suites, so always check the test scripts for errors !)

``````import unittest
from Calculator import Calculator
#Test cases to test Calulator methods
#You always create a child class derived from unittest.TestCase class
class TestCalculator (unittest.TestCase):
#setUp method overridden from the parent class TestCase
def setUp (self):
self.calculator = Calculator ()
...
def test_divide (self):
self.assertEqual (self.calculator.divide (10,2), 6)
# Executing the tests in the above test case class
if __name__ == "__main__":
unittest.main ()``````

When you run this test case, you will get the following result:

``````test_add (__main __. TestCalculator) ... ok
test_divide (__main __. TestCalculator) ... FAIL
test_multiply (__main __. TestCalculator) ... ok
test_subtract (__main __. TestCalculator) ... ok

=================================================== ===================
FAIL: test_divide (__main __. TestCalculator)
-------------------------------------------------- ------------------
Traceback (most recent call last):
File "TestCalculator.py", line 23, in test_divide
self.assertEqual (self.calculator.divide (10,2), 6)
AssertionError: 5.0! = 6

-------------------------------------------------- ------------------
Ran 4 tests in 0.001s

FAILED (failures = 1)``````

It says here that 3 out of 4 tests were successful, but one failed. In a real scenario, it is assumed that your test case is correct, that is, in this way it helps to identify a function that is not properly implemented.