Translated by ChatGPT.
Some Terms
Debugging and Testing
Debugging is generally done by the code's author and is used to check the program's execution process for mismatches between thought process and implementation. Debugging code is usually written alongside the main program and includes error handling and logging. Simple debugging can use print
and assert
statements, while more complex programs can use the logging
module.
Testing, on the other hand, is generally done by a third party. The test code is separated from the program code, and the person writing the tests doesn’t need to understand the exact workings of the program—only whether the program produces the declared output given certain inputs.
Unit Testing
Unit testing is a process that verifies the correctness of a module, function, or class.
The testing method involves writing a set of test cases, where the tester provides various inputs to the module to check if its output is correct, and also verifies if the program correctly identifies and handles invalid inputs by raising exceptions.
Passing all unit tests doesn’t guarantee that the entire program is error-free, but failing any unit test definitely indicates a problem with the program.
Documentation Testing
Documentation provides an overview of a code’s functionality, often with examples that include sample code and expected output. This process is similar to unit testing, except that it's purely descriptive.
Documentation testing is an automated process that finds example code within the documentation, runs it, and compares the output with the documented results.
Integration Testing
Integration testing simulates user behavior by testing the interactions between various modules, ensuring that the program can work properly in a production environment.
Testing in Python
File Structure
In the previous article, I mentioned the src-layout
structure, which is also discussed in an official blog post on setuptools. One advantage of this file structure is that the test folder is typically at the same level as src/
instead of the package itself. This way, you need to install the package in a virtual environment before running the tests, avoiding potential discrepancies between test code and user-downloaded content.
<project_name>
├── LICENSE
├── pyproject.toml
├── README.md
├── src/
│ └── <package_name>/
│ ├── init.py
│ └── example.py
└── tests/
Unit Testing with unittest
unittest
is Python’s built-in unit testing library.
A basic test script looks like this:
import unittest
class TestName1(unittest.TestCase):
def test_sum(self):
self.assertEqual(sum([1, 2, 3]), 6, "Should be 6")
def test_sum_tuple(self):
self.assertEqual(sum((1, 2, 2)), 6, "Should be 6")
class TestName2(unittest.TestCase):
def test_sum(self):
self.assertEqual(sum([1, 2, 3]), 6, "Should be 6")
def test_sum_tuple(self):
self.assertEqual(sum((1, 2, 2)), 6, "Should be 6")
if __name__ == '__main__':
unittest.main()
- The test cases are wrapped in a class that inherits from
unittest.TestCase
. - All test method names begin with "test". Limited properties are testable as they use built-in methods prefixed with
self.
. Some of these are in the table below, with[assertTrue(x)](https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertTrue)
andassertRaises(Exception)
being especially versatile. - When run as
'__main__'
, it executes the built-inunittest.main()
function. - Running the script requires specifying the script filename in Python. If you install the
nose2
library, you can runpython -m nose2
, which will automatically discover and execute all tests. (unittest
also seems to have an automatic discovery feature.)
Documentation Testing
https://www.liaoxuefeng.com/wiki/1016959663602400/1017605739507840
Python's documentation testing uses the doctest
library, as shown below:
class Dict(dict):
'''
Simple dict but also supports access as x.y style.
>>> d1 = Dict()
>>> d1['x'] = 100
>>> d1.x
100
>>> d1.y = 200
>>> d1['y']
200
>>> d2 = Dict(a=1, b=2, c='3')
>>> d2.c
'3'
>>> d2['empty']
Traceback (most recent call last):
...
KeyError: 'empty'
>>> d2.empty
Traceback (most recent call last):
...
AttributeError: 'Dict' object has no attribute 'empty'
'''
def __init__(self, **kw):
super(Dict, self).__init__(**kw)
def __getattr__(self, key):
try:
return self[key]
except KeyError:
raise AttributeError(r"'Dict' object has no attribute '%s'" % key)
def __setattr__(self, key, value):
self[key] = value
if __name__ == '__main__':
import doctest
doctest.testmod()
With docstring
indentation preserved, lines beginning with >>>
are treated as tests, with the following lines as expected outputs. For expected errors, you can use ...
to omit intermediate error details.
Integration Testing
You can also use unittest for integration testing.
The main difference from unit testing is that integration tests often involve setting up a test dataset. This requires overriding unittest.TestCase.setup().
class TestComplexData(unittest.TestCase):
def setUp(self):
# load test data
self.app = App(database='fixtures/test_complex.json')
def test_customer_count(self):
self.assertEqual(len(self.app.customers), 10000)
def test_existence_of_customer(self):
customer = self.app.get_customer(id=9999)
self.assertEqual(customer.name, u"バナナ")
self.assertEqual(customer.address, "10 Red Road, Akihabara, Tokyo")
if __name__ == '__main__':
unittest.main()
See also: