After having seen how to test in R.
Let’s see how to do the same in Python:
Writing a tests-oriented program
A good practice demand that we should try to write our test before we code the program we intended to.
At least, we can try to write the code in a way that is easier to test in the future. Trying to fight out natural tendency to write the tests after your code.
To do that try to follow these guidelines:
- Use independent files:
- One for the program, one for the test
- Code stile for the program:
- A single python file for each task/objective.
- Single functions for each step of your algorithm.
This will help later in the test
- A main function to be invoked. This will list all the functions (steps) of the program.
- Tests, in and out:
- Within the program file
- Tests that define and check the inputs
- Tests that define and check the outputs
- Within the test file
- A test with correct inputs
- Wrong inputs
- Within the program file
main.py, in this file is present the python code we are writing and we need to test.
test_main.py, a file in which, we will write the tests.
In this simple program, it is present a main function (
Main) that contains a series of calls to other methods.
The main, therefore, looks like a series of single steps that calls a single method, that deliver one single operation.
def __IsNameString(name): return isinstance(name, str) def __add(num1, num2): return(num1 + num2) def __output_print(result): if(result > 3): return('Results higher than 3') elif(result == 3): return('Results equal 3') else: raise ValueError('Result not equal to 3') def Main(num1 = 2, num2 = 4): result_add = __add(num1, num2) output_result = __output_print(result_add) if __IsNameString(output_result): print(output_result)
Here in the test file we are going to use the package
unittest that make the test process possible. This package provides a base class
TestCase, that can be used to create the test.
unittest.TestCase in the class, how shown in the class
TestClass. By doing so, we created what is called a
testcase. In which, we can define the test methods, like
The most important component of the testing are the special calls like assertEqual() or assertTrue(). These calls are used to assert statement so the test runner can accumulate all test results and produce a report.
In here, I have used
- assertEqual: This method asses if the result is equal to the one we expect.
- assertTrue:This method asses if the result is a boolean. True in this case/
- assertIsInstance:This method asses if the result is a class. A string in this case
- assertRaises:This method asses if the function raises a warning or n error.
import unittest from main import __IsNameString as isNameString from main import __add as add from main import __output_print as output_print class TestClass(unittest.TestCase): def test_add(self): self.assertEqual(add(3, 4), 7) def test_isNameString(self): self.assertTrue(isNameString("test")) def test_output_print(self): self.assertIsInstance(output_print(2), str) self.assertIsInstance(output_print(2), str) with self.assertRaises(ValueError): output_print(3) if __name__ == '__main__': unittest.main()
The final block shows a simple way to run the tests.
unittest.main() provides a command-line interface to the test script.
We can use the following command line
python -m unittest tests/test_[name_of_test].py
That produces an output that looks like this:
Ran 3 tests in 0.000s.
There are many different calls that can be used for testing, here a provide a list of the one I used the most, more are present in the documentation.
|assertEqual(a, b)||a == b|
|assertNotEqual(a, b)||a != b|
|assertTrue(x)||bool(x) is True|
|assertFalse(x)||bool(x) is False|
|assertIs(a, b)||a is b|
|assertIsNot(a, b)||a is not b|
|assertIsNone(x)||x is None|
|assertIsNotNone(x)||x is not None|
|assertIn(a, b)||a in b|
|assertNotIn(a, b)||a not in b|
|assertIsInstance(a, b)||isinstance(a, b)|
|assertNotIsInstance(a, b)||not isinstance(a, b)|
|assertRaises(exc, fun, *args, **kwds)||fun(*args, **kwds) raises exc|
|assertWarns(warn, fun, *args, **kwds)||fun(*args, **kwds) raises warn|