unittest
- Standard Unit Testing Framework
Basic Usage
import unittest
class MyTestCase(unittest.TestCase):
def test_equal(self):
self.assertEqual('Hello '.capitalize(), 'Hello ')
self.assertNotEqual(1, 2)
def test_true(self):
self.assertTrue('HELLO'.isupper())
self.assertFalse('Hello'.islower())
def test_in(self):
self.assertIn('a', 'abc')
self.assertNotIn('d', 'abc')
def test_is_none(self):
a = None
self.assertIsNone(a)
self.assertIsNotNone('a')
def test_raise(self):
with self.assertRaises(TypeError):
'hello'.capitalize(0)
if __name__ == '__main__':
unittest.main(verbosity=2, catchbreak=True)
Run
All Test Cases
python -m unittest
Specified Modules
python -m unittest [-v] <test_module>
Filter
python -m unittest -k <containing-expr e.g. "MyClass and not method">
Test Fixture
import unittest
class MyTestCase(unittest.TestCase):
def setUp(self):
self.maxDiff = None
def tearDown(self):
pass
Capture Log
import logging
import unittest
class MyTestCase(unittest.TestCase):
def test_log(self):
with self.assertLogs('foo', level='INFO') as cm:
logging.getLogger('foo').info('first message')
logging.getLogger('foo.bar').error('second message')
self.assertEqual(cm.output, ['INFO:foo:first message', 'ERROR:foo.bar:second message'])
if __name__ == '__main__':
unittest.main(verbosity=2, catchbreak=True)
Capture Warnings
import unittest
class MyTestCase(unittest.TestCase):
def test_warning(self):
with self.assertWarns(UserWarning):
do_something()
def test_warning_match(self):
with self.assertWarnsRegex(UserWarning, 'must be 0 or None'):
warnings.warn('value must be 0 or None', UserWarning)
with self.assertWarnsRegex(UserWarning, match=r'must be \d+$'):
warnings.warn('value must be 42', UserWarning)
def test_warning_cm(self):
with self.assertWarns(UserWarning) as cm:
do_something()
self.assertIn('myfile.py', cm.filename)
self.assertEqual(320, cm.lineno)
if __name__ == '__main__':
unittest.main(verbosity=2, catchbreak=True)
Skip Tests
class MyTestCase(unittest.TestCase):
@unittest.skip('demonstrating skipping')
def test_skip(self):
self.fail('shouldn\'t happen')
@unittest.skipIf(sys.version_info < (3, 9), 'python 3.9+ required')
def test_skipif(self):
# Tests that work for only a certain version of Python.
pass
@unittest.skipUnless(sys.platform.startswith('win'), 'requires Windows')
def test_windows_support(self):
# windows specific testing code
pass
def test_maybe_skipped(self):
if not external_resource_available():
self.skipTest('external resource not available')
# test code that depends on the external resource
pass
Expected Failure
class ExpectedFailureTestCase(unittest.TestCase):
@unittest.expectedFailure
def test_fail(self):
self.assertEqual(1, 0, "broken")
Subtest itertaion
class NumbersTest(unittest.TestCase):
def test_even(self):
"""
Test that numbers between 0 and 5 are all even.
"""
for i in range(0, 6):
with self.subTest(i=i):
self.assertEqual(i % 2, 0)
Output:
======================================================================
FAIL: test_even (__main__.NumbersTest) (i=1)
----------------------------------------------------------------------
Traceback (most recent call last):
File "subtests.py", line 32, in test_even
self.assertEqual(i % 2, 0)
AssertionError: 1 != 0
======================================================================
FAIL: test_even (__main__.NumbersTest) (i=3)
----------------------------------------------------------------------
Traceback (most recent call last):
File "subtests.py", line 32, in test_even
self.assertEqual(i % 2, 0)
AssertionError: 1 != 0
======================================================================
FAIL: test_even (__main__.NumbersTest) (i=5)
----------------------------------------------------------------------
Traceback (most recent call last):
File "subtests.py", line 32, in test_even
self.assertEqual(i % 2, 0)
AssertionError: 1 != 0
coroutines tests
from unittest import IsolatedAsyncioTestCase
events = []
class MyTest(IsolatedAsyncioTestCase):
def setUp(self):
events.append("setUp")
async def asyncSetUp(self):
self._async_connection = await AsyncConnection()
events.append("asyncSetUp")
async def test_response(self):
events.append("test_response")
response = await self._async_connection.get("https://dookbook.info")
self.assertEqual(response.status_code, 200)
self.addAsyncCleanup(self.on_cleanup)
def tearDown(self):
events.append("tearDown")
async def asyncTearDown(self):
await self._async_connection.close()
events.append("asyncTearDown")
async def on_cleanup(self):
events.append("cleanup")
if __name__ == '__main__':
unittest.main()
Singal Handling
temporarily remove the control-C handler while the test is being executed:
@unittest.removeHandler
def test_signal_handling(self):
...
or for all (-c
/--catch
option):
python -m unittest -c
python -m unittest -c <test_module>
Mock: unittest.mock
import unittest
import unittest.mock as mock
from io import StringIO
class MyTestCase(unittest.TestCase):
@mock.patch('os.remove')
def test_a(self, mock_os_remove):
import os
filename = 'a'
os_remove_mock.return_value = None
os_remove_mock.side_effect = OSError
os.remove(filename)
mock_os_remove.assert_called_once(filename)
mock_os_remove.assert_called_with(filename)
mock_os_remove.assert_any_call(filename)
self.assertTrue(mock_os_remove.called)
call = mock.call
mock_os_remove.assert_has_calls([call(filename)])
#mock_os_remove.assert_not_called()
@mock.patch('os.getpid')
@mock.patch('os.remove')
def test_b(self, mock_os_remove, mock_os_getpid):
pass
@mock.patch('sys.stderr', new_callable=StringIO)
def test_c(self, mock_stderr):
text = mock_stderr.getvalue()
self.assertEqual(text, 'xxx')
def test_d(self):
mock_opener = mock.mock_open(read_data='aaa')
mock_file = mock_opener()
with mock.patch('builtins.open', mock_opener):
mock_file.write.assert_not_called()
self.assertEqual(mock_file.read.return_value, 'aaa')
mock_opener.assert_called_with(filename, 'w')
mock_file.close.assert_not_called()
References
unittest
Documentationunittest.mock
Documentationunittest.mock
- Getting Started- Test Driven Development - By Examples (2002)