Python unittest assertraises allows you to verify that a certain exception is raised when your code is run. Python’s unittest module provides a set of tools for testing your code. Note that the python unittest assertRaises function does not call the exceptions __init__
method. It can be used to test both your own code and code from third-party libraries.
Contents
- 1 How to Use Python Unittest Assertraises
- 2 Parameters
- 3 Returns:
- 4 Basic usage
- 4.1 Example – 1
- 4.2 Example – 2
- 5 Verifying the Exception Message
- 6 Verifying the Exception Type
- 7 Conclusion
- 8 References
To use python unittest assertRaises
, you pass
in the exception, you expect to be raised as well as a callable
that will execute the code you want to test. assertRaises
will then automatically verify that the correct exception is raised.
In this article, we’ll take a closer look at how to use the assertRaises
method, with some practical examples.
Parameters
assertRaises(exception, callable, *args, **kwds)
exception
– Exception class expected to be raised bycallable
.callable
– Function to be called. Astring
identifying a callable object may also be supplied. Thecallable
will be invoked withargs
andkwds
as appropriate.*args
– Positional arguments passed tocallable
when invoked.**kwds
– Keyword arguments passed tocallable
when invoked.
Returns:
Returns
: The return value from calling callable
(or None
if an exception is not raised).
Basic usage
Example – 1
Let’s start with a very simple example for python unittest assertraises. We have a function that raises a ValueError
if the input value is negative:
def raise_if_negative(value):
if value < 0:
raise ValueError('Value cannot be negative')
We can write a test for this using assertRaises
:
import unittest
class TestRaiseIfNegative(unittest.TestCase):
def test_raise_if_negative(self):
with self.assertRaises(ValueError):
raise_if_negative(-1)
The with
the statement is used here to create a context manager. This allows us to write the test in a more “Pythonic” way, without having to explicitly call the assertRaises method. The advantage of this approach is that it makes the text more readable and easier to maintain.
Let’s see another example:
Example – 2
We have a function that converts a string to uppercase, but it fails if the input string is empty:
def to_upper(value):
if not value:
raise ValueError('Value cannot be empty')
return value.upper()
We can write a test for this using assertRaises
:
import unittest
class TestToUpper(unittest.TestCase):
def test_to_upper(self):
with self.assertRaises(ValueError):
to_upper('')
If you run this test, you’ll see that it fails with the following error message:
AssertionError: ValueError not raised by to_upper
Wait, what?
How can this be? We clearly defined a ValueError
in our function, so why is the test failing?
The reason is that the assertRaises
method only catches exceptions that are raised in the with block. In our case, the to_upper
function is never called, so the exception is never raised.
To fix this, we can explicitly call the function inside the with
block:
import unittest
class TestToUpper(unittest.TestCase):
def test_to_upper(self):
with self.assertRaises(ValueError):
to_upper('')
Now if you run the test, it passes:
PASSED
If you want to be able to call the function outside of the with
block, you can store it in a variable:
import unittest
class TestToUpper(unittest.TestCase):
def test_to_upper(self):
to_upper_fn = to_upper('')
with self.assertRaises(ValueError):
to_upper_fn()
Verifying the Exception Message
In the previous section, we saw how to verify that a certain code path is reached when an exception is raised. But sometimes it’s also important to verify the exception message.
The assertRaises
method allows us to do this by passing it an additional argument: a function that receives the raised exception as an argument and verifies its contents.
Let’s see how this works with our example from the previous section:
import unittest
def test_to_upper(self):
with self.assertRaises(ValueError):
to_upper('')
If we run this test, it fails:
AssertionError: ValueError not raised by to_upper
To fix this, we can pass a function that verifies the exception message:
import unittest
def test_to_upper(self):
with self.assertRaises(ValueError) as cm:
to_upper('')
self.assertEqual(str(cm.exception), 'Value cannot be empty')
The assertEqual
method is used here to verify that the exception message is equal to the expected message.
If you run the test now, it passes:
Verifying the Exception Type
In addition to verifying the exception message, there are also cases where you need to verify the exception type. For example, you might have a function that can raise either a ValueError
or an AttributeError
, and you want to verify that only a ValueError
is raised:
def do_something(value):
if value == 'a':
raise ValueError('Invalid value')
elif value == 'b':
raise AttributeError('Invalid value')
In this case, we can use the assertRaises
the method with multiple arguments:
import unittest
def test_do_something(self):
with self.assertRaises(ValueError):
do_something('a')
with self.assertRaises(AttributeError):
do_something('b')
If we try to call do_something
with a value that doesn’t raise any exception, the test fails:
AssertionError: AttributeError not raised by do_something
To fix this, we can add a third with
block that verifies that no exception is raised:
import unittest
def test_do_something(self):
with self.assertRaises(ValueError):
do_something('a')
with self.assertRaises(AttributeError):
do_something('b')
with self.assertRaises(Exception): # any exception
do_something('c')
If you run the test now, it passes:
PASSED
Conclusion
In this article, we’ve seen how to use python unittest assertRaises
method to write tests that verify that a certain code path is reached when an exception is raised. We’ve also seen how to use this method to verify the exception message and type.
Keep in mind that the assertRaises
method can only be used with exceptions that are raised in the with
block. This means that you might need to explicitly call the function that raises the exception, as we saw in the examples above.
References
- Daniel Perez – assertRaises in Python | Published on Oct 22, 2017
- Python Documentation
- StackOverflow
- TutorialsPoint – UnitTest Framework – Exceptions Test
That’s all for this post. To be up to date with our latest posts follow Python Clear. Let’s learn together!
Source code: Lib/unittest/__init__.py
(If you are already familiar with the basic concepts of testing, you might want
to skip to the list of assert methods.)
The unittest
unit testing framework was originally inspired by JUnit
and has a similar flavor as major unit testing frameworks in other
languages. It supports test automation, sharing of setup and shutdown code
for tests, aggregation of tests into collections, and independence of the
tests from the reporting framework.
To achieve this, unittest
supports some important concepts in an
object-oriented way:
- test fixture
- A test fixture represents the preparation needed to perform one or more
tests, and any associate cleanup actions. This may involve, for example,
creating temporary or proxy databases, directories, or starting a server
process. - test case
- A test case is the individual unit of testing. It checks for a specific
response to a particular set of inputs.unittest
provides a base class,
TestCase
, which may be used to create new test cases. - test suite
- A test suite is a collection of test cases, test suites, or both. It is
used to aggregate tests that should be executed together. - test runner
- A test runner is a component which orchestrates the execution of tests
and provides the outcome to the user. The runner may use a graphical interface,
a textual interface, or return a special value to indicate the results of
executing the tests.
See also
- Module
doctest
- Another test-support module with a very different flavor.
- Simple Smalltalk Testing: With Patterns
- Kent Beck’s original paper on testing frameworks using the pattern shared
byunittest
. - Nose and py.test
- Third-party unittest frameworks with a lighter-weight syntax for writing
tests. For example,assert func(10) == 42
. - The Python Testing Tools Taxonomy
- An extensive list of Python testing tools including functional testing
frameworks and mock object libraries. - Testing in Python Mailing List
- A special-interest-group for discussion of testing, and testing tools,
in Python.
The script Tools/unittestgui/unittestgui.py
in the Python source distribution is
a GUI tool for test discovery and execution. This is intended largely for ease of use
for those new to unit testing. For production environments it is
recommended that tests be driven by a continuous integration system such as
Buildbot, Jenkins
or Hudson.
26.4.1. Basic example¶
The unittest
module provides a rich set of tools for constructing and
running tests. This section demonstrates that a small subset of the tools
suffice to meet the needs of most users.
Here is a short script to test three string methods:
import unittest class TestStringMethods(unittest.TestCase): def test_upper(self): self.assertEqual('foo'.upper(), 'FOO') def test_isupper(self): self.assertTrue('FOO'.isupper()) self.assertFalse('Foo'.isupper()) def test_split(self): s = 'hello world' self.assertEqual(s.split(), ['hello', 'world']) # check that s.split fails when the separator is not a string with self.assertRaises(TypeError): s.split(2) if __name__ == '__main__': unittest.main()
A testcase is created by subclassing unittest.TestCase
. The three
individual tests are defined with methods whose names start with the letters
test
. This naming convention informs the test runner about which methods
represent tests.
The crux of each test is a call to assertEqual()
to check for an
expected result; assertTrue()
or assertFalse()
to verify a condition; or assertRaises()
to verify that a
specific exception gets raised. These methods are used instead of the
assert
statement so the test runner can accumulate all test results
and produce a report.
The setUp()
and tearDown()
methods allow you
to define instructions that will be executed before and after each test method.
They are covered in more detail in the section Organizing test code.
The final block shows a simple way to run the tests. unittest.main()
provides a command-line interface to the test script. When run from the command
line, the above script produces an output that looks like this:
... ---------------------------------------------------------------------- Ran 3 tests in 0.000s OK
Passing the -v
option to your test script will instruct unittest.main()
to enable a higher level of verbosity, and produce the following output:
test_isupper (__main__.TestStringMethods) ... ok test_split (__main__.TestStringMethods) ... ok test_upper (__main__.TestStringMethods) ... ok ---------------------------------------------------------------------- Ran 3 tests in 0.001s OK
The above examples show the most commonly used unittest
features which
are sufficient to meet many everyday testing needs. The remainder of the
documentation explores the full feature set from first principles.
26.4.2. Command-Line Interface¶
The unittest module can be used from the command line to run tests from
modules, classes or even individual test methods:
python -m unittest test_module1 test_module2 python -m unittest test_module.TestClass python -m unittest test_module.TestClass.test_method
You can pass in a list with any combination of module names, and fully
qualified class or method names.
Test modules can be specified by file path as well:
python -m unittest tests/test_something.py
This allows you to use the shell filename completion to specify the test module.
The file specified must still be importable as a module. The path is converted
to a module name by removing the ‘.py’ and converting path separators into ‘.’.
If you want to execute a test file that isn’t importable as a module you should
execute the file directly instead.
You can run tests with more detail (higher verbosity) by passing in the -v flag:
python -m unittest -v test_module
When executed without arguments Test Discovery is started:
For a list of all the command-line options:
Changed in version 3.2: In earlier versions it was only possible to run individual test methods and
not modules or classes.
26.4.2.1. Command-line options¶
unittest supports these command-line options:
-
-b
,
--buffer
¶
-
The standard output and standard error streams are buffered during the test
run. Output during a passing test is discarded. Output is echoed normally
on test fail or error and is added to the failure messages.
-
-c
,
--catch
¶
-
Control-C
during the test run waits for the current test to end and then
reports all the results so far. A secondControl-C
raises the normal
KeyboardInterrupt
exception.See Signal Handling for the functions that provide this functionality.
-
-f
,
--failfast
¶
-
Stop the test run on the first error or failure.
-
--locals
¶
-
Show local variables in tracebacks.
New in version 3.2: The command-line options -b
, -c
and -f
were added.
New in version 3.5: The command-line option --locals
.
The command line can also be used for test discovery, for running all of the
tests in a project or just a subset.
26.4.3. Test Discovery¶
New in version 3.2.
Unittest supports simple test discovery. In order to be compatible with test
discovery, all of the test files must be modules or
packages (including namespace packages) importable from the top-level directory of
the project (this means that their filenames must be valid identifiers).
Test discovery is implemented in TestLoader.discover()
, but can also be
used from the command line. The basic command-line usage is:
cd project_directory python -m unittest discover
Note
As a shortcut, python -m unittest
is the equivalent of
python -m unittest discover
. If you want to pass arguments to test
discovery the discover
sub-command must be used explicitly.
The discover
sub-command has the following options:
-
-v
,
--verbose
¶
-
Verbose output
-
-s
,
--start-directory
directory
¶ -
Directory to start discovery (
.
default)
-
-p
,
--pattern
pattern
¶ -
Pattern to match test files (
test*.py
default)
-
-t
,
--top-level-directory
directory
¶ -
Top level directory of project (defaults to start directory)
The -s
, -p
, and -t
options can be passed in
as positional arguments in that order. The following two command lines
are equivalent:
python -m unittest discover -s project_directory -p "*_test.py" python -m unittest discover project_directory "*_test.py"
As well as being a path it is possible to pass a package name, for example
myproject.subpackage.test
, as the start directory. The package name you
supply will then be imported and its location on the filesystem will be used
as the start directory.
Caution
Test discovery loads tests by importing them. Once test discovery has found
all the test files from the start directory you specify it turns the paths
into package names to import. For example foo/bar/baz.py
will be
imported as foo.bar.baz
.
If you have a package installed globally and attempt test discovery on
a different copy of the package then the import could happen from the
wrong place. If this happens test discovery will warn you and exit.
If you supply the start directory as a package name rather than a
path to a directory then discover assumes that whichever location it
imports from is the location you intended, so you will not get the
warning.
Test modules and packages can customize test loading and discovery by through
the load_tests protocol.
26.4.4. Organizing test code¶
The basic building blocks of unit testing are test cases — single
scenarios that must be set up and checked for correctness. In unittest
,
test cases are represented by unittest.TestCase
instances.
To make your own test cases you must write subclasses of
TestCase
or use FunctionTestCase
.
The testing code of a TestCase
instance should be entirely self
contained, such that it can be run either in isolation or in arbitrary
combination with any number of other test cases.
The simplest TestCase
subclass will simply implement a test method
(i.e. a method whose name starts with test
) in order to perform specific
testing code:
import unittest class DefaultWidgetSizeTestCase(unittest.TestCase): def test_default_widget_size(self): widget = Widget('The widget') self.assertEqual(widget.size(), (50, 50))
Note that in order to test something, we use one of the assert*()
methods provided by the TestCase
base class. If the test fails, an
exception will be raised, and unittest
will identify the test case as a
failure. Any other exceptions will be treated as errors.
Tests can be numerous, and their set-up can be repetitive. Luckily, we
can factor out set-up code by implementing a method called
setUp()
, which the testing framework will automatically
call for every single test we run:
import unittest class WidgetTestCase(unittest.TestCase): def setUp(self): self.widget = Widget('The widget') def test_default_widget_size(self): self.assertEqual(self.widget.size(), (50,50), 'incorrect default size') def test_widget_resize(self): self.widget.resize(100,150) self.assertEqual(self.widget.size(), (100,150), 'wrong size after resize')
Note
The order in which the various tests will be run is determined
by sorting the test method names with respect to the built-in
ordering for strings.
If the setUp()
method raises an exception while the test is
running, the framework will consider the test to have suffered an error, and
the test method will not be executed.
Similarly, we can provide a tearDown()
method that tidies up
after the test method has been run:
import unittest class WidgetTestCase(unittest.TestCase): def setUp(self): self.widget = Widget('The widget') def tearDown(self): self.widget.dispose()
If setUp()
succeeded, tearDown()
will be
run whether the test method succeeded or not.
Such a working environment for the testing code is called a fixture.
Test case instances are grouped together according to the features they test.
unittest
provides a mechanism for this: the test suite,
represented by unittest
‘s TestSuite
class. In most cases,
calling unittest.main()
will do the right thing and collect all the
module’s test cases for you, and then execute them.
However, should you want to customize the building of your test suite,
you can do it yourself:
def suite(): suite = unittest.TestSuite() suite.addTest(WidgetTestCase('test_default_widget_size')) suite.addTest(WidgetTestCase('test_widget_resize')) return suite if __name__ == '__main__': runner = unittest.TextTestRunner() runner.run(suite())
You can place the definitions of test cases and test suites in the same modules
as the code they are to test (such as widget.py
), but there are several
advantages to placing the test code in a separate module, such as
test_widget.py
:
- The test module can be run standalone from the command line.
- The test code can more easily be separated from shipped code.
- There is less temptation to change test code to fit the code it tests without
a good reason. - Test code should be modified much less frequently than the code it tests.
- Tested code can be refactored more easily.
- Tests for modules written in C must be in separate modules anyway, so why not
be consistent? - If the testing strategy changes, there is no need to change the source code.
26.4.5. Re-using old test code¶
Some users will find that they have existing test code that they would like to
run from unittest
, without converting every old test function to a
TestCase
subclass.
For this reason, unittest
provides a FunctionTestCase
class.
This subclass of TestCase
can be used to wrap an existing test
function. Set-up and tear-down functions can also be provided.
Given the following test function:
def testSomething(): something = makeSomething() assert something.name is not None # ...
one can create an equivalent test case instance as follows, with optional
set-up and tear-down methods:
testcase = unittest.FunctionTestCase(testSomething, setUp=makeSomethingDB, tearDown=deleteSomethingDB)
Note
Even though FunctionTestCase
can be used to quickly convert an
existing test base over to a unittest
-based system, this approach is
not recommended. Taking the time to set up proper TestCase
subclasses will make future test refactorings infinitely easier.
In some cases, the existing tests may have been written using the doctest
module. If so, doctest
provides a DocTestSuite
class that can
automatically build unittest.TestSuite
instances from the existing
doctest
-based tests.
26.4.6. Skipping tests and expected failures¶
New in version 3.1.
Unittest supports skipping individual test methods and even whole classes of
tests. In addition, it supports marking a test as an “expected failure,” a test
that is broken and will fail, but shouldn’t be counted as a failure on a
TestResult
.
Skipping a test is simply a matter of using the skip()
decorator
or one of its conditional variants.
Basic skipping looks like this:
class MyTestCase(unittest.TestCase): @unittest.skip("demonstrating skipping") def test_nothing(self): self.fail("shouldn't happen") @unittest.skipIf(mylib.__version__ < (1, 3), "not supported in this library version") def test_format(self): # Tests that work for only a certain version of the library. pass @unittest.skipUnless(sys.platform.startswith("win"), "requires Windows") def test_windows_support(self): # windows specific testing code pass
This is the output of running the example above in verbose mode:
test_format (__main__.MyTestCase) ... skipped 'not supported in this library version' test_nothing (__main__.MyTestCase) ... skipped 'demonstrating skipping' test_windows_support (__main__.MyTestCase) ... skipped 'requires Windows' ---------------------------------------------------------------------- Ran 3 tests in 0.005s OK (skipped=3)
Classes can be skipped just like methods:
@unittest.skip("showing class skipping") class MySkippedTestCase(unittest.TestCase): def test_not_run(self): pass
TestCase.setUp()
can also skip the test. This is useful when a resource
that needs to be set up is not available.
Expected failures use the expectedFailure()
decorator.
class ExpectedFailureTestCase(unittest.TestCase): @unittest.expectedFailure def test_fail(self): self.assertEqual(1, 0, "broken")
It’s easy to roll your own skipping decorators by making a decorator that calls
skip()
on the test when it wants it to be skipped. This decorator skips
the test unless the passed object has a certain attribute:
def skipUnlessHasattr(obj, attr): if hasattr(obj, attr): return lambda func: func return unittest.skip("{!r} doesn't have {!r}".format(obj, attr))
The following decorators implement test skipping and expected failures:
-
@
unittest.
skip
(reason)¶ -
Unconditionally skip the decorated test. reason should describe why the
test is being skipped.
-
@
unittest.
skipIf
(condition, reason)¶ -
Skip the decorated test if condition is true.
-
@
unittest.
skipUnless
(condition, reason)¶ -
Skip the decorated test unless condition is true.
-
@
unittest.
expectedFailure
¶ -
Mark the test as an expected failure. If the test fails when run, the test
is not counted as a failure.
-
exception
unittest.
SkipTest
(reason)¶ -
This exception is raised to skip a test.
Usually you can use
TestCase.skipTest()
or one of the skipping
decorators instead of raising this directly.
Skipped tests will not have setUp()
or tearDown()
run around them.
Skipped classes will not have setUpClass()
or tearDownClass()
run.
Skipped modules will not have setUpModule()
or tearDownModule()
run.
26.4.7. Distinguishing test iterations using subtests¶
New in version 3.4.
When some of your tests differ only by a some very small differences, for
instance some parameters, unittest allows you to distinguish them inside
the body of a test method using the subTest()
context manager.
For example, the following test:
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)
will produce the following 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
Without using a subtest, execution would stop after the first failure,
and the error would be less easy to diagnose because the value of i
wouldn’t be displayed:
====================================================================== FAIL: test_even (__main__.NumbersTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "subtests.py", line 32, in test_even self.assertEqual(i % 2, 0) AssertionError: 1 != 0
26.4.8. Classes and functions¶
This section describes in depth the API of unittest
.
26.4.8.1. Test cases¶
-
class
unittest.
TestCase
(methodName=’runTest’)¶ -
Instances of the
TestCase
class represent the logical test units
in theunittest
universe. This class is intended to be used as a base
class, with specific tests being implemented by concrete subclasses. This class
implements the interface needed by the test runner to allow it to drive the
tests, and methods that the test code can use to check for and report various
kinds of failure.Each instance of
TestCase
will run a single base method: the method
named methodName.
In most uses ofTestCase
, you will neither change
the methodName nor reimplement the defaultrunTest()
method.Changed in version 3.2:
TestCase
can be instantiated successfully without providing a
methodName. This makes it easier to experiment withTestCase
from the interactive interpreter.TestCase
instances provide three groups of methods: one group used
to run the test, another used by the test implementation to check conditions
and report failures, and some inquiry methods allowing information about the
test itself to be gathered.Methods in the first group (running the test) are:
-
setUp
()¶ -
Method called to prepare the test fixture. This is called immediately
before calling the test method; other thanAssertionError
orSkipTest
,
any exception raised by this method will be considered an error rather than
a test failure. The default implementation does nothing.
-
tearDown
()¶ -
Method called immediately after the test method has been called and the
result recorded. This is called even if the test method raised an
exception, so the implementation in subclasses may need to be particularly
careful about checking internal state. Any exception, other than
AssertionError
orSkipTest
, raised by this method will be
considered an additional error rather than a test failure (thus increasing
the total number of reported errors). This method will only be called if
thesetUp()
succeeds, regardless of the outcome of the test method.
The default implementation does nothing.
-
setUpClass
()¶ -
A class method called before tests in an individual class run.
setUpClass
is called with the class as the only argument
and must be decorated as aclassmethod()
:@classmethod def setUpClass(cls): ...
See Class and Module Fixtures for more details.
New in version 3.2.
-
tearDownClass
()¶ -
A class method called after tests in an individual class have run.
tearDownClass
is called with the class as the only argument
and must be decorated as aclassmethod()
:@classmethod def tearDownClass(cls): ...
See Class and Module Fixtures for more details.
New in version 3.2.
-
run
(result=None)¶ -
Run the test, collecting the result into the
TestResult
object
passed as result. If result is omitted orNone
, a temporary
result object is created (by calling thedefaultTestResult()
method) and used. The result object is returned torun()
‘s
caller.The same effect may be had by simply calling the
TestCase
instance.Changed in version 3.3: Previous versions of
run
did not return the result. Neither did
calling an instance.
-
skipTest
(reason)¶ -
Calling this during a test method or
setUp()
skips the current
test. See Skipping tests and expected failures for more information.New in version 3.1.
-
subTest
(msg=None, **params)¶ -
Return a context manager which executes the enclosed code block as a
subtest. msg and params are optional, arbitrary values which are
displayed whenever a subtest fails, allowing you to identify them
clearly.A test case can contain any number of subtest declarations, and
they can be arbitrarily nested.See Distinguishing test iterations using subtests for more information.
New in version 3.4.
-
debug
()¶ -
Run the test without collecting the result. This allows exceptions raised
by the test to be propagated to the caller, and can be used to support
running tests under a debugger.
The
TestCase
class provides several assert methods to check for and
report failures. The following table lists the most commonly used methods
(see the tables below for more assert methods):Method Checks that New in 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
3.1 assertIsNot(a, b)
a is not b
3.1 assertIsNone(x)
x is None
3.1 assertIsNotNone(x)
x is not None
3.1 assertIn(a, b)
a in b
3.1 assertNotIn(a, b)
a not in b
3.1 assertIsInstance(a, b)
isinstance(a, b)
3.2 assertNotIsInstance(a, b)
not isinstance(a, b)
3.2 All the assert methods accept a msg argument that, if specified, is used
as the error message on failure (see alsolongMessage
).
Note that the msg keyword argument can be passed toassertRaises()
,
assertRaisesRegex()
,assertWarns()
,assertWarnsRegex()
only when they are used as a context manager.-
assertEqual
(first, second, msg=None)¶ -
Test that first and second are equal. If the values do not
compare equal, the test will fail.In addition, if first and second are the exact same type and one of
list, tuple, dict, set, frozenset or str or any type that a subclass
registers withaddTypeEqualityFunc()
the type-specific equality
function will be called in order to generate a more useful default
error message (see also the list of type-specific methods).Changed in version 3.1: Added the automatic calling of type-specific equality function.
Changed in version 3.2:
assertMultiLineEqual()
added as the default type equality
function for comparing strings.
-
assertNotEqual
(first, second, msg=None)¶ -
Test that first and second are not equal. If the values do
compare equal, the test will fail.
-
assertTrue
(expr, msg=None)¶ -
assertFalse
(expr, msg=None)¶ -
Test that expr is true (or false).
Note that this is equivalent to
bool(expr) is True
and not toexpr
(use
is TrueassertIs(expr, True)
for the latter). This method
should also be avoided when more specific methods are available (e.g.
assertEqual(a, b)
instead ofassertTrue(a == b)
), because they
provide a better error message in case of failure.
-
assertIs
(first, second, msg=None)¶ -
assertIsNot
(first, second, msg=None)¶ -
Test that first and second evaluate (or don’t evaluate) to the
same object.New in version 3.1.
-
assertIsNone
(expr, msg=None)¶ -
assertIsNotNone
(expr, msg=None)¶ -
Test that expr is (or is not)
None
.New in version 3.1.
-
assertIn
(first, second, msg=None)¶ -
assertNotIn
(first, second, msg=None)¶ -
Test that first is (or is not) in second.
New in version 3.1.
-
assertIsInstance
(obj, cls, msg=None)¶ -
assertNotIsInstance
(obj, cls, msg=None)¶ -
Test that obj is (or is not) an instance of cls (which can be a
class or a tuple of classes, as supported byisinstance()
).
To check for the exact type, useassertIs(type(obj), cls)
.New in version 3.2.
It is also possible to check the production of exceptions, warnings, and
log messages using the following methods:Method Checks that New in assertRaises(exc, fun, *args, **kwds)
fun(*args, **kwds)
raises excassertRaisesRegex(exc, r, fun, *args, **kwds)
fun(*args, **kwds)
raises exc
and the message matches regex r3.1 assertWarns(warn, fun, *args, **kwds)
fun(*args, **kwds)
raises warn3.2 assertWarnsRegex(warn, r, fun, *args, **kwds)
fun(*args, **kwds)
raises warn
and the message matches regex r3.2 assertLogs(logger, level)
The with
block logs on logger
with minimum level3.4 -
assertRaises
(exception, callable, *args, **kwds)¶ -
assertRaises
(exception, msg=None) -
Test that an exception is raised when callable is called with any
positional or keyword arguments that are also passed to
assertRaises()
. The test passes if exception is raised, is an
error if another exception is raised, or fails if no exception is raised.
To catch any of a group of exceptions, a tuple containing the exception
classes may be passed as exception.If only the exception and possibly the msg arguments are given,
return a context manager so that the code under test can be written
inline rather than as a function:with self.assertRaises(SomeException): do_something()
When used as a context manager,
assertRaises()
accepts the
additional keyword argument msg.The context manager will store the caught exception object in its
exception
attribute. This can be useful if the intention
is to perform additional checks on the exception raised:with self.assertRaises(SomeException) as cm: do_something() the_exception = cm.exception self.assertEqual(the_exception.error_code, 3)
Changed in version 3.1: Added the ability to use
assertRaises()
as a context manager.Changed in version 3.2: Added the
exception
attribute.Changed in version 3.3: Added the msg keyword argument when used as a context manager.
-
assertRaisesRegex
(exception, regex, callable, *args, **kwds)¶ -
assertRaisesRegex
(exception, regex, msg=None) -
Like
assertRaises()
but also tests that regex matches
on the string representation of the raised exception. regex may be
a regular expression object or a string containing a regular expression
suitable for use byre.search()
. Examples:self.assertRaisesRegex(ValueError, "invalid literal for.*XYZ'$", int, 'XYZ')
or:
with self.assertRaisesRegex(ValueError, 'literal'): int('XYZ')
New in version 3.1: under the name
assertRaisesRegexp
.Changed in version 3.2: Renamed to
assertRaisesRegex()
.Changed in version 3.3: Added the msg keyword argument when used as a context manager.
-
assertWarns
(warning, callable, *args, **kwds)¶ -
assertWarns
(warning, msg=None) -
Test that a warning is triggered when callable is called with any
positional or keyword arguments that are also passed to
assertWarns()
. The test passes if warning is triggered and
fails if it isn’t. Any exception is an error.
To catch any of a group of warnings, a tuple containing the warning
classes may be passed as warnings.If only the warning and possibly the msg arguments are given,
return a context manager so that the code under test can be written
inline rather than as a function:with self.assertWarns(SomeWarning): do_something()
When used as a context manager,
assertWarns()
accepts the
additional keyword argument msg.The context manager will store the caught warning object in its
warning
attribute, and the source line which triggered the
warnings in thefilename
andlineno
attributes.
This can be useful if the intention is to perform additional checks
on the warning caught:with self.assertWarns(SomeWarning) as cm: do_something() self.assertIn('myfile.py', cm.filename) self.assertEqual(320, cm.lineno)
This method works regardless of the warning filters in place when it
is called.New in version 3.2.
Changed in version 3.3: Added the msg keyword argument when used as a context manager.
-
assertWarnsRegex
(warning, regex, callable, *args, **kwds)¶ -
assertWarnsRegex
(warning, regex, msg=None) -
Like
assertWarns()
but also tests that regex matches on the
message of the triggered warning. regex may be a regular expression
object or a string containing a regular expression suitable for use
byre.search()
. Example:self.assertWarnsRegex(DeprecationWarning, r'legacy_function() is deprecated', legacy_function, 'XYZ')
or:
with self.assertWarnsRegex(RuntimeWarning, 'unsafe frobnicating'): frobnicate('/etc/passwd')
New in version 3.2.
Changed in version 3.3: Added the msg keyword argument when used as a context manager.
-
assertLogs
(logger=None, level=None)¶ -
A context manager to test that at least one message is logged on
the logger or one of its children, with at least the given
level.If given, logger should be a
logging.Logger
object or a
str
giving the name of a logger. The default is the root
logger, which will catch all messages.If given, level should be either a numeric logging level or
its string equivalent (for example either"ERROR"
or
logging.ERROR
). The default islogging.INFO
.The test passes if at least one message emitted inside the
with
block matches the logger and level conditions, otherwise it fails.The object returned by the context manager is a recording helper
which keeps tracks of the matching log messages. It has two
attributes:-
records
¶ -
A list of
logging.LogRecord
objects of the matching
log messages.
-
output
¶ -
A list of
str
objects with the formatted output of
matching messages.
Example:
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'])
New in version 3.4.
-
There are also other methods used to perform more specific checks, such as:
Method Checks that New in assertAlmostEqual(a, b)
round(a-b, 7) == 0
assertNotAlmostEqual(a, b)
round(a-b, 7) != 0
assertGreater(a, b)
a > b
3.1 assertGreaterEqual(a, b)
a >= b
3.1 assertLess(a, b)
a < b
3.1 assertLessEqual(a, b)
a <= b
3.1 assertRegex(s, r)
r.search(s)
3.1 assertNotRegex(s, r)
not r.search(s)
3.2 assertCountEqual(a, b)
a and b have the same
elements in the same number,
regardless of their order3.2 -
assertAlmostEqual
(first, second, places=7, msg=None, delta=None)¶ -
assertNotAlmostEqual
(first, second, places=7, msg=None, delta=None)¶ -
Test that first and second are approximately (or not approximately)
equal by computing the difference, rounding to the given number of
decimal places (default 7), and comparing to zero. Note that these
methods round the values to the given number of decimal places (i.e.
like theround()
function) and not significant digits.If delta is supplied instead of places then the difference
between first and second must be less or equal to (or greater than) delta.Supplying both delta and places raises a
TypeError
.Changed in version 3.2:
assertAlmostEqual()
automatically considers almost equal objects
that compare equal.assertNotAlmostEqual()
automatically fails
if the objects compare equal. Added the delta keyword argument.
-
assertGreater
(first, second, msg=None)¶ -
assertGreaterEqual
(first, second, msg=None)¶ -
assertLess
(first, second, msg=None)¶ -
assertLessEqual
(first, second, msg=None)¶ -
Test that first is respectively >, >=, < or <= than second depending
on the method name. If not, the test will fail:>>> self.assertGreaterEqual(3, 4) AssertionError: "3" unexpectedly not greater than or equal to "4"
New in version 3.1.
-
assertRegex
(text, regex, msg=None)¶ -
assertNotRegex
(text, regex, msg=None)¶ -
Test that a regex search matches (or does not match) text. In case
of failure, the error message will include the pattern and the text (or
the pattern and the part of text that unexpectedly matched). regex
may be a regular expression object or a string containing a regular
expression suitable for use byre.search()
.New in version 3.1: under the name
assertRegexpMatches
.Changed in version 3.2: The method
assertRegexpMatches()
has been renamed to
assertRegex()
.New in version 3.2:
assertNotRegex()
.New in version 3.5: The name
assertNotRegexpMatches
is a deprecated alias
forassertNotRegex()
.
-
assertCountEqual
(first, second, msg=None)¶ -
Test that sequence first contains the same elements as second,
regardless of their order. When they don’t, an error message listing the
differences between the sequences will be generated.Duplicate elements are not ignored when comparing first and
second. It verifies whether each element has the same count in both
sequences. Equivalent to:
assertEqual(Counter(list(first)), Counter(list(second)))
but works with sequences of unhashable objects as well.New in version 3.2.
The
assertEqual()
method dispatches the equality check for objects of
the same type to different type-specific methods. These methods are already
implemented for most of the built-in types, but it’s also possible to
register new methods usingaddTypeEqualityFunc()
:-
addTypeEqualityFunc
(typeobj, function)¶ -
Registers a type-specific method called by
assertEqual()
to check
if two objects of exactly the same typeobj (not subclasses) compare
equal. function must take two positional arguments and a third msg=None
keyword argument just asassertEqual()
does. It must raise
self.failureException(msg)
when inequality
between the first two parameters is detected – possibly providing useful
information and explaining the inequalities in details in the error
message.New in version 3.1.
The list of type-specific methods automatically used by
assertEqual()
are summarized in the following table. Note
that it’s usually not necessary to invoke these methods directly.Method Used to compare New in assertMultiLineEqual(a, b)
strings 3.1 assertSequenceEqual(a, b)
sequences 3.1 assertListEqual(a, b)
lists 3.1 assertTupleEqual(a, b)
tuples 3.1 assertSetEqual(a, b)
sets or frozensets 3.1 assertDictEqual(a, b)
dicts 3.1 -
assertMultiLineEqual
(first, second, msg=None)¶ -
Test that the multiline string first is equal to the string second.
When not equal a diff of the two strings highlighting the differences
will be included in the error message. This method is used by default
when comparing strings withassertEqual()
.New in version 3.1.
-
assertSequenceEqual
(first, second, msg=None, seq_type=None)¶ -
Tests that two sequences are equal. If a seq_type is supplied, both
first and second must be instances of seq_type or a failure will
be raised. If the sequences are different an error message is
constructed that shows the difference between the two.This method is not called directly by
assertEqual()
, but
it’s used to implementassertListEqual()
and
assertTupleEqual()
.New in version 3.1.
-
assertListEqual
(first, second, msg=None)¶ -
assertTupleEqual
(first, second, msg=None)¶ -
Tests that two lists or tuples are equal. If not, an error message is
constructed that shows only the differences between the two. An error
is also raised if either of the parameters are of the wrong type.
These methods are used by default when comparing lists or tuples with
assertEqual()
.New in version 3.1.
-
assertSetEqual
(first, second, msg=None)¶ -
Tests that two sets are equal. If not, an error message is constructed
that lists the differences between the sets. This method is used by
default when comparing sets or frozensets withassertEqual()
.Fails if either of first or second does not have a
set.difference()
method.New in version 3.1.
-
assertDictEqual
(first, second, msg=None)¶ -
Test that two dictionaries are equal. If not, an error message is
constructed that shows the differences in the dictionaries. This
method will be used by default to compare dictionaries in
calls toassertEqual()
.New in version 3.1.
Finally the
TestCase
provides the following methods and attributes:-
fail
(msg=None)¶ -
Signals a test failure unconditionally, with msg or
None
for
the error message.
-
failureException
¶ -
This class attribute gives the exception raised by the test method. If a
test framework needs to use a specialized exception, possibly to carry
additional information, it must subclass this exception in order to “play
fair” with the framework. The initial value of this attribute is
AssertionError
.
-
longMessage
¶ -
This class attribute determines what happens when a custom failure message
is passed as the msg argument to an assertXYY call that fails.
True
is the default value. In this case, the custom message is appended
to the end of the standard failure message.
When set toFalse
, the custom message replaces the standard message.The class setting can be overridden in individual test methods by assigning
an instance attribute, self.longMessage, toTrue
orFalse
before
calling the assert methods.The class setting gets reset before each test call.
New in version 3.1.
-
maxDiff
¶ -
This attribute controls the maximum length of diffs output by assert
methods that report diffs on failure. It defaults to 80*8 characters.
Assert methods affected by this attribute are
assertSequenceEqual()
(including all the sequence comparison
methods that delegate to it),assertDictEqual()
and
assertMultiLineEqual()
.Setting
maxDiff
toNone
means that there is no maximum length of
diffs.New in version 3.2.
Testing frameworks can use the following methods to collect information on
the test:-
countTestCases
()¶ -
Return the number of tests represented by this test object. For
TestCase
instances, this will always be1
.
-
defaultTestResult
()¶ -
Return an instance of the test result class that should be used for this
test case class (if no other result instance is provided to the
run()
method).For
TestCase
instances, this will always be an instance of
TestResult
; subclasses ofTestCase
should override this
as necessary.
-
id
()¶ -
Return a string identifying the specific test case. This is usually the
full name of the test method, including the module and class name.
-
shortDescription
()¶ -
Returns a description of the test, or
None
if no description
has been provided. The default implementation of this method
returns the first line of the test method’s docstring, if available,
orNone
.Changed in version 3.1: In 3.1 this was changed to add the test name to the short description
even in the presence of a docstring. This caused compatibility issues
with unittest extensions and adding the test name was moved to the
TextTestResult
in Python 3.2.
-
addCleanup
(function, *args, **kwargs)¶ -
Add a function to be called after
tearDown()
to cleanup resources
used during the test. Functions will be called in reverse order to the
order they are added (LIFO). They
are called with any arguments and keyword arguments passed into
addCleanup()
when they are added.If
setUp()
fails, meaning thattearDown()
is not called,
then any cleanup functions added will still be called.New in version 3.1.
-
doCleanups
()¶ -
This method is called unconditionally after
tearDown()
, or
aftersetUp()
ifsetUp()
raises an exception.It is responsible for calling all the cleanup functions added by
addCleanup()
. If you need cleanup functions to be called
prior totearDown()
then you can calldoCleanups()
yourself.doCleanups()
pops methods off the stack of cleanup
functions one at a time, so it can be called at any time.New in version 3.1.
-
-
class
unittest.
FunctionTestCase
(testFunc, setUp=None, tearDown=None, description=None)¶ -
This class implements the portion of the
TestCase
interface which
allows the test runner to drive the test, but does not provide the methods
which test code can use to check and report errors. This is used to create
test cases using legacy test code, allowing it to be integrated into a
unittest
-based test framework.
26.4.8.1.1. Deprecated aliases¶
For historical reasons, some of the TestCase
methods had one or more
aliases that are now deprecated. The following table lists the correct names
along with their deprecated aliases:
Method Name Deprecated alias Deprecated alias assertEqual()
failUnlessEqual assertEquals assertNotEqual()
failIfEqual assertNotEquals assertTrue()
failUnless assert_ assertFalse()
failIf assertRaises()
failUnlessRaises assertAlmostEqual()
failUnlessAlmostEqual assertAlmostEquals assertNotAlmostEqual()
failIfAlmostEqual assertNotAlmostEquals assertRegex()
assertRegexpMatches assertNotRegex()
assertNotRegexpMatches assertRaisesRegex()
assertRaisesRegexp Deprecated since version 3.1: the fail* aliases listed in the second column.
Deprecated since version 3.2: the assert* aliases listed in the third column.
Deprecated since version 3.2:
assertRegexpMatches
andassertRaisesRegexp
have been renamed to
assertRegex()
andassertRaisesRegex()
.Deprecated since version 3.5: the
assertNotRegexpMatches
name in favor ofassertNotRegex()
.
26.4.8.2. Grouping tests¶
-
class
unittest.
TestSuite
(tests=())¶ -
This class represents an aggregation of individual test cases and test suites.
The class presents the interface needed by the test runner to allow it to be run
as any other test case. Running aTestSuite
instance is the same as
iterating over the suite, running each test individually.If tests is given, it must be an iterable of individual test cases or other
test suites that will be used to build the suite initially. Additional methods
are provided to add test cases and suites to the collection later on.TestSuite
objects behave much likeTestCase
objects, except
they do not actually implement a test. Instead, they are used to aggregate
tests into groups of tests that should be run together. Some additional
methods are available to add tests toTestSuite
instances:-
addTest
(test)¶ -
Add a
TestCase
orTestSuite
to the suite.
-
addTests
(tests)¶ -
Add all the tests from an iterable of
TestCase
andTestSuite
instances to this test suite.This is equivalent to iterating over tests, calling
addTest()
for
each element.
TestSuite
shares the following methods withTestCase
:-
run
(result)¶ -
Run the tests associated with this suite, collecting the result into the
test result object passed as result. Note that unlike
TestCase.run()
,TestSuite.run()
requires the result object to
be passed in.
-
debug
()¶ -
Run the tests associated with this suite without collecting the
result. This allows exceptions raised by the test to be propagated to the
caller and can be used to support running tests under a debugger.
-
countTestCases
()¶ -
Return the number of tests represented by this test object, including all
individual tests and sub-suites.
-
__iter__
()¶ -
Tests grouped by a
TestSuite
are always accessed by iteration.
Subclasses can lazily provide tests by overriding__iter__()
. Note
that this method may be called several times on a single suite (for
example when counting tests or comparing for equality) so the tests
returned by repeated iterations beforeTestSuite.run()
must be the
same for each call iteration. AfterTestSuite.run()
, callers should
not rely on the tests returned by this method unless the caller uses a
subclass that overridesTestSuite._removeTestAtIndex()
to preserve
test references.Changed in version 3.2: In earlier versions the
TestSuite
accessed tests directly rather
than through iteration, so overriding__iter__()
wasn’t sufficient
for providing tests.Changed in version 3.4: In earlier versions the
TestSuite
held references to each
TestCase
afterTestSuite.run()
. Subclasses can restore
that behavior by overridingTestSuite._removeTestAtIndex()
.
In the typical usage of a
TestSuite
object, therun()
method
is invoked by aTestRunner
rather than by the end-user test harness. -
26.4.8.3. Loading and running tests¶
-
class
unittest.
TestLoader
¶ -
The
TestLoader
class is used to create test suites from classes and
modules. Normally, there is no need to create an instance of this class; the
unittest
module provides an instance that can be shared as
unittest.defaultTestLoader
. Using a subclass or instance, however,
allows customization of some configurable properties.TestLoader
objects have the following attributes:-
errors
¶ -
A list of the non-fatal errors encountered while loading tests. Not reset
by the loader at any point. Fatal errors are signalled by the relevant
a method raising an exception to the caller. Non-fatal errors are also
indicated by a synthetic test that will raise the original error when
run.New in version 3.5.
TestLoader
objects have the following methods:-
loadTestsFromTestCase
(testCaseClass)¶ -
Return a suite of all test cases contained in the
TestCase
-derived
testCaseClass
.A test case instance is created for each method named by
getTestCaseNames()
. By default these are the method names
beginning withtest
. IfgetTestCaseNames()
returns no
methods, but therunTest()
method is implemented, a single test
case is created for that method instead.
-
loadTestsFromModule
(module, pattern=None)¶ -
Return a suite of all test cases contained in the given module. This
method searches module for classes derived fromTestCase
and
creates an instance of the class for each test method defined for the
class.Note
While using a hierarchy of
TestCase
-derived classes can be
convenient in sharing fixtures and helper functions, defining test
methods on base classes that are not intended to be instantiated
directly does not play well with this method. Doing so, however, can
be useful when the fixtures are different and defined in subclasses.If a module provides a
load_tests
function it will be called to
load the tests. This allows modules to customize test loading.
This is the load_tests protocol. The pattern argument is passed as
the third argument toload_tests
.Changed in version 3.2: Support for
load_tests
added.Changed in version 3.5: The undocumented and unofficial use_load_tests default argument is
deprecated and ignored, although it is still accepted for backward
compatibility. The method also now accepts a keyword-only argument
pattern which is passed toload_tests
as the third argument.
-
loadTestsFromName
(name, module=None)¶ -
Return a suite of all test cases given a string specifier.
The specifier name is a “dotted name” that may resolve either to a
module, a test case class, a test method within a test case class, a
TestSuite
instance, or a callable object which returns a
TestCase
orTestSuite
instance. These checks are
applied in the order listed here; that is, a method on a possible test
case class will be picked up as “a test method within a test case class”,
rather than “a callable object”.For example, if you have a module
SampleTests
containing a
TestCase
-derived classSampleTestCase
with three test
methods (test_one()
,test_two()
, andtest_three()
), the
specifier'SampleTests.SampleTestCase'
would cause this method to
return a suite which will run all three test methods. Using the specifier
'SampleTests.SampleTestCase.test_two'
would cause it to return a test
suite which will run only thetest_two()
test method. The specifier
can refer to modules and packages which have not been imported; they will
be imported as a side-effect.The method optionally resolves name relative to the given module.
Changed in version 3.5: If an
ImportError
orAttributeError
occurs while traversing
name then a synthetic test that raises that error when run will be
returned. These errors are included in the errors accumulated by
self.errors.
-
loadTestsFromNames
(names, module=None)¶ -
Similar to
loadTestsFromName()
, but takes a sequence of names rather
than a single name. The return value is a test suite which supports all
the tests defined for each name.
-
getTestCaseNames
(testCaseClass)¶ -
Return a sorted sequence of method names found within testCaseClass;
this should be a subclass ofTestCase
.
-
discover
(start_dir, pattern=’test*.py’, top_level_dir=None)¶ -
Find all the test modules by recursing into subdirectories from the
specified start directory, and return a TestSuite object containing them.
Only test files that match pattern will be loaded. (Using shell style
pattern matching.) Only module names that are importable (i.e. are valid
Python identifiers) will be loaded.All test modules must be importable from the top level of the project. If
the start directory is not the top level directory then the top level
directory must be specified separately.If importing a module fails, for example due to a syntax error, then
this will be recorded as a single error and discovery will continue. If
the import failure is due toSkipTest
being raised, it will be
recorded as a skip instead of an error.If a package (a directory containing a file named
__init__.py
) is
found, the package will be checked for aload_tests
function. If this
exists then it will be called
package.load_tests(loader, tests, pattern)
. Test discovery takes care
to ensure that a package is only checked for tests once during an
invocation, even if the load_tests function itself calls
loader.discover
.If
load_tests
exists then discovery does not recurse into the
package,load_tests
is responsible for loading all tests in the
package.The pattern is deliberately not stored as a loader attribute so that
packages can continue discovery themselves. top_level_dir is stored so
load_tests
does not need to pass this argument in to
loader.discover()
.start_dir can be a dotted module name as well as a directory.
New in version 3.2.
Changed in version 3.4: Modules that raise
SkipTest
on import are recorded as skips,
not errors.
Discovery works for namespace packages.
Paths are sorted before being imported so that execution order is
the same even if the underlying file system’s ordering is not
dependent on file name.Changed in version 3.5: Found packages are now checked for
load_tests
regardless of
whether their path matches pattern, because it is impossible for
a package name to match the default pattern.
The following attributes of a
TestLoader
can be configured either by
subclassing or assignment on an instance:-
testMethodPrefix
¶ -
String giving the prefix of method names which will be interpreted as test
methods. The default value is'test'
.This affects
getTestCaseNames()
and all theloadTestsFrom*()
methods.
-
sortTestMethodsUsing
¶ -
Function to be used to compare method names when sorting them in
getTestCaseNames()
and all theloadTestsFrom*()
methods.
-
suiteClass
¶ -
Callable object that constructs a test suite from a list of tests. No
methods on the resulting object are needed. The default value is the
TestSuite
class.This affects all the
loadTestsFrom*()
methods.
-
-
class
unittest.
TestResult
¶ -
This class is used to compile information about which tests have succeeded
and which have failed.A
TestResult
object stores the results of a set of tests. The
TestCase
andTestSuite
classes ensure that results are
properly recorded; test authors do not need to worry about recording the
outcome of tests.Testing frameworks built on top of
unittest
may want access to the
TestResult
object generated by running a set of tests for reporting
purposes; aTestResult
instance is returned by the
TestRunner.run()
method for this purpose.TestResult
instances have the following attributes that will be of
interest when inspecting the results of running a set of tests:-
errors
¶ -
A list containing 2-tuples of
TestCase
instances and strings
holding formatted tracebacks. Each tuple represents a test which raised an
unexpected exception.
-
failures
¶ -
A list containing 2-tuples of
TestCase
instances and strings
holding formatted tracebacks. Each tuple represents a test where a failure
was explicitly signalled using theTestCase.assert*()
methods.
-
skipped
¶ -
A list containing 2-tuples of
TestCase
instances and strings
holding the reason for skipping the test.New in version 3.1.
-
expectedFailures
¶ -
A list containing 2-tuples of
TestCase
instances and strings
holding formatted tracebacks. Each tuple represents an expected failure
of the test case.
-
unexpectedSuccesses
¶ -
A list containing
TestCase
instances that were marked as expected
failures, but succeeded.
-
shouldStop
¶ -
Set to
True
when the execution of tests should stop bystop()
.
-
testsRun
¶ -
The total number of tests run so far.
-
buffer
¶ -
If set to true,
sys.stdout
andsys.stderr
will be buffered in between
startTest()
andstopTest()
being called. Collected output will
only be echoed onto the realsys.stdout
andsys.stderr
if the test
fails or errors. Any output is also attached to the failure / error message.New in version 3.2.
-
failfast
¶ -
If set to true
stop()
will be called on the first failure or error,
halting the test run.New in version 3.2.
-
tb_locals
¶ -
If set to true then local variables will be shown in tracebacks.
New in version 3.5.
-
wasSuccessful
()¶ -
Return
True
if all tests run so far have passed, otherwise returns
False
.Changed in version 3.4: Returns
False
if there were anyunexpectedSuccesses
from tests marked with theexpectedFailure()
decorator.
-
stop
()¶ -
This method can be called to signal that the set of tests being run should
be aborted by setting theshouldStop
attribute toTrue
.
TestRunner
objects should respect this flag and return without
running any additional tests.For example, this feature is used by the
TextTestRunner
class to
stop the test framework when the user signals an interrupt from the
keyboard. Interactive tools which provideTestRunner
implementations can use this in a similar manner.
The following methods of the
TestResult
class are used to maintain
the internal data structures, and may be extended in subclasses to support
additional reporting requirements. This is particularly useful in building
tools which support interactive reporting while tests are being run.-
startTest
(test)¶ -
Called when the test case test is about to be run.
-
stopTest
(test)¶ -
Called after the test case test has been executed, regardless of the
outcome.
-
startTestRun
()¶ -
Called once before any tests are executed.
New in version 3.1.
-
stopTestRun
()¶ -
Called once after all tests are executed.
New in version 3.1.
-
addError
(test, err)¶ -
Called when the test case test raises an unexpected exception. err is a
tuple of the form returned bysys.exc_info()
:(type, value,
.
traceback)The default implementation appends a tuple
(test, formatted_err)
to
the instance’serrors
attribute, where formatted_err is a
formatted traceback derived from err.
-
addFailure
(test, err)¶ -
Called when the test case test signals a failure. err is a tuple of
the form returned bysys.exc_info()
:(type, value, traceback)
.The default implementation appends a tuple
(test, formatted_err)
to
the instance’sfailures
attribute, where formatted_err is a
formatted traceback derived from err.
-
addSuccess
(test)¶ -
Called when the test case test succeeds.
The default implementation does nothing.
-
addSkip
(test, reason)¶ -
Called when the test case test is skipped. reason is the reason the
test gave for skipping.The default implementation appends a tuple
(test, reason)
to the
instance’sskipped
attribute.
-
addExpectedFailure
(test, err)¶ -
Called when the test case test fails, but was marked with the
expectedFailure()
decorator.The default implementation appends a tuple
(test, formatted_err)
to
the instance’sexpectedFailures
attribute, where formatted_err
is a formatted traceback derived from err.
-
addUnexpectedSuccess
(test)¶ -
Called when the test case test was marked with the
expectedFailure()
decorator, but succeeded.The default implementation appends the test to the instance’s
unexpectedSuccesses
attribute.
-
addSubTest
(test, subtest, outcome)¶ -
Called when a subtest finishes. test is the test case
corresponding to the test method. subtest is a custom
TestCase
instance describing the subtest.If outcome is
None
, the subtest succeeded. Otherwise,
it failed with an exception where outcome is a tuple of the form
returned bysys.exc_info()
:(type, value, traceback)
.The default implementation does nothing when the outcome is a
success, and records subtest failures as normal failures.New in version 3.4.
-
-
class
unittest.
TextTestResult
(stream, descriptions, verbosity)¶ -
A concrete implementation of
TestResult
used by the
TextTestRunner
.New in version 3.2: This class was previously named
_TextTestResult
. The old name still
exists as an alias but is deprecated.
-
unittest.
defaultTestLoader
¶ -
Instance of the
TestLoader
class intended to be shared. If no
customization of theTestLoader
is needed, this instance can be used
instead of repeatedly creating new instances.
-
class
unittest.
TextTestRunner
(stream=None, descriptions=True, verbosity=1, failfast=False, buffer=False, resultclass=None, warnings=None, *, tb_locals=False)¶ -
A basic test runner implementation that outputs results to a stream. If stream
isNone
, the default,sys.stderr
is used as the output stream. This class
has a few configurable parameters, but is essentially very simple. Graphical
applications which run test suites should provide alternate implementations. Such
implementations should accept**kwargs
as the interface to construct runners
changes when features are added to unittest.By default this runner shows
DeprecationWarning
,
PendingDeprecationWarning
,ResourceWarning
and
ImportWarning
even if they are ignored by default. Deprecation warnings caused by deprecated unittest
methods are also special-cased and, when the warning
filters are'default'
or'always'
, they will appear only once
per-module, in order to avoid too many warning messages. This behavior can
be overridden using Python’s-Wd
or-Wa
options
(see Warning control) and leaving
warnings toNone
.Changed in version 3.2: Added the
warnings
argument.Changed in version 3.2: The default stream is set to
sys.stderr
at instantiation time rather
than import time.Changed in version 3.5: Added the tb_locals parameter.
-
_makeResult
()¶ -
This method returns the instance of
TestResult
used byrun()
.
It is not intended to be called directly, but can be overridden in
subclasses to provide a customTestResult
._makeResult()
instantiates the class or callable passed in the
TextTestRunner
constructor as theresultclass
argument. It
defaults toTextTestResult
if noresultclass
is provided.
The result class is instantiated with the following arguments:stream, descriptions, verbosity
-
run
(test)¶ -
This method is the main public interface to the TextTestRunner. This
method takes aTestSuite
orTestCase
instance. A
TestResult
is created by calling
_makeResult()
and the test(s) are run and the
results printed to stdout.
-
-
unittest.
main
(module=’__main__’, defaultTest=None, argv=None, testRunner=None, testLoader=unittest.defaultTestLoader, exit=True, verbosity=1, failfast=None, catchbreak=None, buffer=None, warnings=None)¶ -
A command-line program that loads a set of tests from module and runs them;
this is primarily for making test modules conveniently executable.
The simplest use for this function is to include the following line at the
end of a test script:if __name__ == '__main__': unittest.main()
You can run tests with more detailed information by passing in the verbosity
argument:if __name__ == '__main__': unittest.main(verbosity=2)
The defaultTest argument is either the name of a single test or an
iterable of test names to run if no test names are specified via argv. If
not specified orNone
and no test names are provided via argv, all
tests found in module are run.The argv argument can be a list of options passed to the program, with the
first element being the program name. If not specified orNone
,
the values ofsys.argv
are used.The testRunner argument can either be a test runner class or an already
created instance of it. By defaultmain
callssys.exit()
with
an exit code indicating success or failure of the tests run.The testLoader argument has to be a
TestLoader
instance,
and defaults todefaultTestLoader
.main
supports being used from the interactive interpreter by passing in the
argumentexit=False
. This displays the result on standard output without
callingsys.exit()
:>>> from unittest import main >>> main(module='test_module', exit=False)
The failfast, catchbreak and buffer parameters have the same
effect as the same-name command-line options.The warnings argument specifies the warning filter
that should be used while running the tests. If it’s not specified, it will
remainNone
if a-W
option is passed to python
(see Warning control),
otherwise it will be set to'default'
.Calling
main
actually returns an instance of theTestProgram
class.
This stores the result of the tests run as theresult
attribute.Changed in version 3.1: The exit parameter was added.
Changed in version 3.2: The verbosity, failfast, catchbreak, buffer
and warnings parameters were added.Changed in version 3.4: The defaultTest parameter was changed to also accept an iterable of
test names.
26.4.8.3.1. load_tests Protocol¶
New in version 3.2.
Modules or packages can customize how tests are loaded from them during normal
test runs or test discovery by implementing a function called load_tests
.
If a test module defines load_tests
it will be called by
TestLoader.loadTestsFromModule()
with the following arguments:
load_tests(loader, standard_tests, pattern)
where pattern is passed straight through from loadTestsFromModule
. It
defaults to None
.
It should return a TestSuite
.
loader is the instance of TestLoader
doing the loading.
standard_tests are the tests that would be loaded by default from the
module. It is common for test modules to only want to add or remove tests
from the standard set of tests.
The third argument is used when loading packages as part of test discovery.
A typical load_tests
function that loads tests from a specific set of
TestCase
classes may look like:
test_cases = (TestCase1, TestCase2, TestCase3) def load_tests(loader, tests, pattern): suite = TestSuite() for test_class in test_cases: tests = loader.loadTestsFromTestCase(test_class) suite.addTests(tests) return suite
If discovery is started in a directory containing a package, either from the
command line or by calling TestLoader.discover()
, then the package
__init__.py
will be checked for load_tests
. If that function does
not exist, discovery will recurse into the package as though it were just
another directory. Otherwise, discovery of the package’s tests will be left up
to load_tests
which is called with the following arguments:
load_tests(loader, standard_tests, pattern)
This should return a TestSuite
representing all the tests
from the package. (standard_tests
will only contain tests
collected from __init__.py
.)
Because the pattern is passed into load_tests
the package is free to
continue (and potentially modify) test discovery. A ‘do nothing’
load_tests
function for a test package would look like:
def load_tests(loader, standard_tests, pattern): # top level directory cached on loader instance this_dir = os.path.dirname(__file__) package_tests = loader.discover(start_dir=this_dir, pattern=pattern) standard_tests.addTests(package_tests) return standard_tests
Changed in version 3.5: Discovery no longer checks package names for matching pattern due to the
impossibility of package names matching the default pattern.
26.4.9. Class and Module Fixtures¶
Class and module level fixtures are implemented in TestSuite
. When
the test suite encounters a test from a new class then tearDownClass()
from the previous class (if there is one) is called, followed by
setUpClass()
from the new class.
Similarly if a test is from a different module from the previous test then
tearDownModule
from the previous module is run, followed by
setUpModule
from the new module.
After all the tests have run the final tearDownClass
and
tearDownModule
are run.
Note that shared fixtures do not play well with [potential] features like test
parallelization and they break test isolation. They should be used with care.
The default ordering of tests created by the unittest test loaders is to group
all tests from the same modules and classes together. This will lead to
setUpClass
/ setUpModule
(etc) being called exactly once per class and
module. If you randomize the order, so that tests from different modules and
classes are adjacent to each other, then these shared fixture functions may be
called multiple times in a single test run.
Shared fixtures are not intended to work with suites with non-standard
ordering. A BaseTestSuite
still exists for frameworks that don’t want to
support shared fixtures.
If there are any exceptions raised during one of the shared fixture functions
the test is reported as an error. Because there is no corresponding test
instance an _ErrorHolder
object (that has the same interface as a
TestCase
) is created to represent the error. If you are just using
the standard unittest test runner then this detail doesn’t matter, but if you
are a framework author it may be relevant.
26.4.9.1. setUpClass and tearDownClass¶
These must be implemented as class methods:
import unittest class Test(unittest.TestCase): @classmethod def setUpClass(cls): cls._connection = createExpensiveConnectionObject() @classmethod def tearDownClass(cls): cls._connection.destroy()
If you want the setUpClass
and tearDownClass
on base classes called
then you must call up to them yourself. The implementations in
TestCase
are empty.
If an exception is raised during a setUpClass
then the tests in the class
are not run and the tearDownClass
is not run. Skipped classes will not
have setUpClass
or tearDownClass
run. If the exception is a
SkipTest
exception then the class will be reported as having been skipped
instead of as an error.
26.4.9.2. setUpModule and tearDownModule¶
These should be implemented as functions:
def setUpModule(): createConnection() def tearDownModule(): closeConnection()
If an exception is raised in a setUpModule
then none of the tests in the
module will be run and the tearDownModule
will not be run. If the exception is a
SkipTest
exception then the module will be reported as having been skipped
instead of as an error.
26.4.10. Signal Handling¶
New in version 3.2.
The -c/--catch
command-line option to unittest,
along with the catchbreak
parameter to unittest.main()
, provide
more friendly handling of control-C during a test run. With catch break
behavior enabled control-C will allow the currently running test to complete,
and the test run will then end and report all the results so far. A second
control-c will raise a KeyboardInterrupt
in the usual way.
The control-c handling signal handler attempts to remain compatible with code or
tests that install their own signal.SIGINT
handler. If the unittest
handler is called but isn’t the installed signal.SIGINT
handler,
i.e. it has been replaced by the system under test and delegated to, then it
calls the default handler. This will normally be the expected behavior by code
that replaces an installed handler and delegates to it. For individual tests
that need unittest
control-c handling disabled the removeHandler()
decorator can be used.
There are a few utility functions for framework authors to enable control-c
handling functionality within test frameworks.
-
unittest.
installHandler
()¶ -
Install the control-c handler. When a
signal.SIGINT
is received
(usually in response to the user pressing control-c) all registered results
havestop()
called.
-
unittest.
registerResult
(result)¶ -
Register a
TestResult
object for control-c handling. Registering a
result stores a weak reference to it, so it doesn’t prevent the result from
being garbage collected.Registering a
TestResult
object has no side-effects if control-c
handling is not enabled, so test frameworks can unconditionally register
all results they create independently of whether or not handling is enabled.
-
unittest.
removeResult
(result)¶ -
Remove a registered result. Once a result has been removed then
stop()
will no longer be called on that result object in
response to a control-c.
-
unittest.
removeHandler
(function=None)¶ -
When called without arguments this function removes the control-c handler
if it has been installed. This function can also be used as a test decorator
to temporarily remove the handler whilst the test is being executed:@unittest.removeHandler def test_signal_handling(self): ...
Python Unit Testing Tutorial
This is an update to Doug Hellman’s excellent PyMOTW article found here:
- PyMOTW — unittest (2007)
The code and examples here have been updated by Corey Goldberg to reflect Python 3.3.
further reading:
- unittest — Python Standard Library 3.3 Documentation
unittest — Automated testing framework
Python’s unittest
module, sometimes referred to as ‘PyUnit’, is based on the XUnit framework design by Kent Beck and Erich Gamma. The same pattern is repeated in many other languages, including C, Perl, Java, and Smalltalk. The framework implemented by unittest
supports fixtures, test suites, and a test runner to enable automated testing for your code.
Basic Test Structure
Tests, as defined by unittest, have two parts: code to manage test «fixtures», and the test itself. Individual tests are created by subclassing TestCase
and overriding or adding appropriate methods. For example,
import unittest class SimplisticTest(unittest.TestCase): def test(self): self.assertTrue(True) if __name__ == '__main__': unittest.main()
In this case, the SimplisticTest
has a single test()
method, which would fail if True is ever False.
Running Tests
The easiest way to run unittest tests is to include:
if __name__ == '__main__': unittest.main()
at the bottom of each test file, then simply run the script directly from the command line:
$ python3 test_simple.py
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
This abbreviated output includes the amount of time the tests took, along with a status indicator for each test (the «.» on the first line of output means that a test passed). For more detailed test results, include the -v
option:
$ python3 test_simple.py -v
test (__main__.SimplisticTest) ... ok
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
Test Outcomes
Tests have 3 possible outcomes:
The test passes.
The test does not pass, and raises an AssertionError
exception.
The test raises an exception other than AssertionError
.
There is no explicit way to cause a test to «pass», so a test’s status depends on the presence (or absence) of an exception.
import unittest class OutcomesTest(unittest.TestCase): def test_pass(self): self.assertTrue(True) def test_fail(self): self.assertTrue(False) def test_error(self): raise RuntimeError('Test error!') if __name__ == '__main__': unittest.main()
When a test fails or generates an error, the traceback is included in the output.
$ python3 test_outcomes.py
EF.
======================================================================
ERROR: test_error (__main__.OutcomesTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test_outcomes.py", line 13, in test_error
raise RuntimeError('Test error!')
RuntimeError: Test error!
======================================================================
FAIL: test_fail (__main__.OutcomesTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test_outcomes.py", line 9, in test_fail
self.assertTrue(False)
AssertionError: False is not true
----------------------------------------------------------------------
Ran 3 tests in 0.000s
FAILED (failures=1, errors=1)
In the example above, test_fail()
fails and the traceback shows the line with the failure code. It is up to the person reading the test output to look at the code to figure out the semantic meaning of the failed test, though. To make it easier to understand the nature of a test failure, the assert*()
methods all accept an argument msg
, which can be used to produce a more detailed error message.
import unittest class FailureMessageTest(unittest.TestCase): def test_fail(self): self.assertTrue(False, 'failure message goes here') if __name__ == '__main__': unittest.main()
$ python3 test_failwithmessage.py -v
test_fail (__main__.FailureMessageTest) ... FAIL
======================================================================
FAIL: test_fail (__main__.FailureMessageTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test_failwithmessage.py", line 6, in test_fail
self.assertTrue(False, 'failure message goes here')
AssertionError: failure message goes here
----------------------------------------------------------------------
Ran 1 test in 0.000s
FAILED (failures=1)
Asserting Truth
Most tests assert the truth of some condition. There are a few different ways to write truth-checking tests, depending on the perspective of the test author and the desired outcome of the code being tested. If the code produces a value which can be evaluated as true, the method assertTrue()
should be used. If the code produces a false value, the method assertFalse()
makes more sense.
import unittest class TruthTest(unittest.TestCase): def test_assert_true(self): self.assertTrue(True) def test_assert_false(self): self.assertFalse(False) if __name__ == '__main__': unittest.main()
$ python3 test_truth.py -v
test_assert_false (__main__.TruthTest) ... ok
test_assert_true (__main__.TruthTest) ... ok
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
Assertion Methods
The TestCase
class provides a number of methods to check for and report failures:
TestCase
classassert*
methods
Common Assertions
Method |
---|
assertTrue(x, msg=None) |
assertFalse(x, msg=None) |
assertIsNone(x, msg=None) |
assertIsNotNone(x, msg=None) |
assertEqual(a, b, msg=None) |
assertNotEqual(a, b, msg=None) |
assertIs(a, b, msg=None) |
assertIsNot(a, b, msg=None) |
assertIn(a, b, msg=None) |
assertNotIn(a, b, msg=None) |
assertIsInstance(a, b, msg=None) |
assertNotIsInstance(a, b, msg=None) |
Other Assertions
Method |
---|
assertAlmostEqual(a, b, places=7, msg=None, delta=None) |
assertNotAlmostEqual(a, b, places=7, msg=None, delta=None) |
assertGreater(a, b, msg=None) |
assertGreaterEqual(a, b, msg=None) |
assertLess(a, b, msg=None) |
assertLessEqual(a, b, msg=None) |
assertRegex(text, regexp, msg=None) |
assertNotRegex(text, regexp, msg=None) |
assertCountEqual(a, b, msg=None) |
assertMultiLineEqual(a, b, msg=None) |
assertSequenceEqual(a, b, msg=None) |
assertListEqual(a, b, msg=None) |
assertTupleEqual(a, b, msg=None) |
assertSetEqual(a, b, msg=None) |
assertDictEqual(a, b, msg=None) |
Failure Messages
These assertions are handy, since the values being compared appear in the failure message when a test fails.
import unittest class InequalityTest(unittest.TestCase): def testEqual(self): self.assertNotEqual(1, 3-2) def testNotEqual(self): self.assertEqual(2, 3-2) if __name__ == '__main__': unittest.main()
And when these tests are run:
$ python3 test_notequal.py -v
testEqual (__main__.InequalityTest) ... FAIL
testNotEqual (__main__.InequalityTest) ... FAIL
======================================================================
FAIL: test_equal (__main__.InequalityTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test_notequal.py", line 7, in test_equal
self.assertNotEqual(1, 3-2)
AssertionError: 1 == 1
======================================================================
FAIL: test_not_equal (__main__.InequalityTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test_notequal.py", line 10, in test_not_equal
self.assertEqual(2, 3-2)
AssertionError: 2 != 1
----------------------------------------------------------------------
Ran 2 tests in 0.000s
FAILED (failures=2)
All the assert methods above accept a msg
argument that, if specified, is used as the error message on failure.
Testing for Exceptions (and Warnings)
The TestCase
class provides methods to check for expected exceptions:
TestCase
classassert*
methods
Method |
---|
assertRaises(exception) |
assertRaisesRegex(exception, regexp) |
assertWarns(warn, fun, *args, **kwds) |
assertWarnsRegex(warn, fun, *args, **kwds) |
As previously mentioned, if a test raises an exception other than AssertionError
it is treated as an error. This is very useful for uncovering mistakes while you are modifying code which has existing test coverage. There are circumstances, however, in which you want the test to verify that some code does produce an exception. For example, if an invalid value is given to an attribute of an object. In such cases, assertRaises()
makes the code more clear than trapping the exception yourself. Compare these two tests:
import unittest def raises_error(*args, **kwds): raise ValueError('Invalid value: %s%s' % (args, kwds)) class ExceptionTest(unittest.TestCase): def test_trap_locally(self): try: raises_error('a', b='c') except ValueError: pass else: self.fail('Did not see ValueError') def test_assert_raises(self): self.assertRaises(ValueError, raises_error, 'a', b='c') if __name__ == '__main__': unittest.main()
The results for both are the same, but the second test using assertRaises()
is more succinct.
$ python3 test_exception.py -v
test_assert_raises (__main__.ExceptionTest) ... ok
test_trap_locally (__main__.ExceptionTest) ... ok
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
Test Fixtures
Fixtures are resources needed by a test. For example, if you are writing several tests for the same class, those tests all need an instance of that class to use for testing. Other test fixtures include database connections and temporary files (many people would argue that using external resources makes such tests not «unit» tests, but they are still tests and still useful). TestCase
includes a special hook to configure and clean up any fixtures needed by your tests. To configure the fixtures, override setUp()
. To clean up, override tearDown()
.
import unittest class FixturesTest(unittest.TestCase): def setUp(self): print('In setUp()') self.fixture = range(1, 10) def tearDown(self): print('In tearDown()') del self.fixture def test(self): print('in test()') self.assertEqual(self.fixture, range(1, 10)) if __name__ == '__main__': unittest.main()
When this sample test is run, you can see the order of execution of the fixture and test methods:
$ python3 test_fixtures.py
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
In setUp()
in test()
In tearDown()
Test Suites
The standard library documentation describes how to organize test suites manually. I generally do not use test suites directly, because I prefer to build the suites automatically (these are automated tests, after all). Automating the construction of test suites is especially useful for large code bases, in which related tests are not all in the same place. Tools such as nose make it easier to manage tests when they are spread over multiple files and directories.
Введение
Написание тестов — тема, которую поначалу многие игнорируют, такой вывод я делаю в первую очередь по себе, и по некоторым людям, от которых я слышал что-либо касательно тестирования. Мне было немного лень садиться за изучение библиотек для тестирования, хотя я к любому виду программирования отношусь с большим трепетом, но именно тестирование хотелось постоянно откладывать.
Совершенно очевидно, что тестирование программ это важно и что не напрасно для этого существует столько инструментов. Ответьте на вопрос: что доказывает факт работы программы? Успешное тестирование. Потому что другого способа доказать это попросту не существует.
И на самом деле, написание тестов к маленьким программам, которые вы пишите во время начала своего обучения, не имеет как таковой ценности для этих программ, ведь если программа принимает на вход строку X, а на выходе отдает строку X, с добавленным перед ней словом ‘Hello’, то скорее всего она не нуждается в тестировании. Но как и в случае с прочими темами, учиться тестированию следует с малого, поэтому для постепенного погружения в тему мы начнем писать тесты именно для тех программ, которые в тестировании на самом деле и не нуждаются.
unittest
Начнем мы знакомство с тестированием с библиотеки unittest. Эта библиотека поставляется со стандартным набором библиотек, поэтому устанавливать ничего не придется.
Документация
Но перед написанием тестов давайте немного подготовим рабочее пространство. Первым делом создадим новый проект и установим виртуальное окружение.
$ python3 -m venv venv $ source venv/bin/activate
Добавим в проект git и сразу оформим файл .gitignore, создадим репозиторий и соединим его с проектом.
$ git init $ git add . $ git commit -m "v1.0" $ git branch -M main $ git remote add origin (ссылка на репозиторий) $ git push -u origin main
Создадим папку
$ mkdir tests
И в папке tests создадим файл __init__.py
__init__.py нужен, чтобы папка tests воспринималась как пакет
В итоге содержание будет таким
$ ls tests venv
Файл .gitignore не показывается по команде ls, для этого есть команда
$ ls -a . .. .git .gitignore .idea tests venv
В папке tests создадим папку examples
Перейдем в папку examples
$ cd examples/ /examples$ mkdir unittest /examples$ ls unittest
И создадим папку unittest
С подготовкой закончено.
Первые тесты
Теперь перейдем в папку unittest и напишем первую программу для тестирования.
resume.py
import time def candidate(item): """Информация о кандидате""" new_candidate = {'Кандидат': item} time.sleep(2) return new_candidate def resume(): """Сценарий сбора информации о кандидате""" print('Давайте познакомимся') time.sleep(1) full_name = input('Как вас зовут? ').title() age = input(f'Приятно познакомиться, {full_name}, сколько вам лет?: ') city = input('И последни вопрос, откуда вы?: ') time.sleep(1) print('Спасибо за беседу, передал информацию о вашей кандидатуре руководству') info = f'имя - {full_name}, возраст - {age}, город - {city}' return info def main(): print('n', candidate(resume()), sep='') if __name__ == '__main__': main()
RUN
Давайте познакомимся Как вас зовут? Илья Приятно познакомиться, Илья, сколько вам лет?: 24 И последни вопрос, откуда вы?: Москва Спасибо за беседу, передал информацию о вашей кандидатуре руководству {'Кандидат': 'имя - Илья, возраст - 24, город - Москва'} Process finished with exit code 0
Например такую. Программа resume.py будет содержать две функции, resume() — будет имитировать общение с человеком, candidate() — возвращать собранную информацию. В самой программе нет ничего необычного, метод .sleep() модуля time нужен для небольших пауз, это никак не сказывается на функционале, просто, как по мне, добавляет немного натуральности процессу. В resume() мы возвращаем строку из собранных данных, в candidate() мы передаем эту строку в качестве ключа для словаря и возвращаем этот словарь. Оборачиваем программу в функцию main() и запускаем ее внутри конструкции if __name__ == ‘__main__’.
Как видно из запуска resume.py работает так как и описано, и эта программа как раз относится к тем, которые не имеет большого смысла тестировать, потому что тут все совсем очевидно и не понятно, что тут может пойти не так. Тем не менее давайте ее проверим.
tests/test_resume.py
import unittest from examples.unittest import resume class ResumeTests(unittest.TestCase): """Тесты для программы resume.py""" def test_candidate_resume(self): """Тест функции candidate(). Возвращаемые данные верны?""" formatted_info_about_candidate = resume.candidate('имя - Илья, возраст - 25, город - Москва') self.assertEqual(formatted_info_about_candidate, {'Кандидат': 'имя - Илья, возраст - 25, город - Москва'}) if __name__ == '__main__': unittest.main()
Модуль unittest предполагает строгое соблюдение нескольких условий
1. Все тесты должны называться со слова test_. После test_ обычно пишется название функции (в названии допускаются уточнения логики функции), которую мы тестируем. Если функция будет не соответствовать этому условию, то при запуске тестирования unittest попросту не увидит этот тест.
2. Все тесты хранятся в классе, который наследуется от класса TestCase. Собственно в связи с этим и связано первое правило, логика TestCase предполагает наличие слова test_. При этом наличие слова Tests или Test в названии класса, унаследованного от TestCase, условие не обязательное.
Теперь что касается самого теста. Тест на данный момент в классе ResumeTests только один — test_candidate_resume. В данном тесте мы тестируем функцию candidate(), внутрь этой функции мы передаем какие-то данные, и сохраняем работу функции в переменную formatted_info_about_candidate. Эта переменная будет объектом для сравнения результата.
Второй строчкой происходит это самое сравнение. Метод assertEqual принимает два значения и проверяет их на условие равенства (A == B). Таким образом, в качестве A передаем результат работы функции candidate(), а в качестве B результат, который мы предполагаем увидеть.
Запустить программу можно с помощью Run (ctrl + shift + f10), но чаще используется метод запуска через терминал. Этот способ наиболее практичен, поскольку так нам не надо каждый раз открывать программу с тестом для проверки программы. Так что будем использовать именно метод запуска через терминал.
Terminal
$ python -m unittest . ---------------------------------------------------------------------- Ran 1 test in 2.001s OK
Запустить тесты можно командой python -m unittes. Первая строчка результата содержит точку, эта точка означает, что один тест прошел успешно. Ниже информация о времени исполнения теста и еще ниже слово OK, которе является свидетельством успеха теста.
Мы протестировали функцию candidate(). Но что делать с функцией resume()? Если у вас возникла мысль, что проверять можно только функции, которые принимают какие-нибудь параметры, то это, конечно, не так. Метод assertEqual всего один из ряда методов для проверки условий, хотя проверить функцию resume() безусловно можно и с помощью assertEqual.
Давайте проверим, например, являются ли, возвращаемые функцией resume(), данные типом str.
tests/test_resume.py
import unittest from examples.unittest import resume class ResumeTests(unittest.TestCase): """Тесты для программы resume.py""" def test_candidate_resume(self): """Тест функции candidate(). Возвращаемые данные верны?""" formatted_info_about_candidate = resume.candidate('имя - Илья, возраст - 25, город - Москва') self.assertEqual(formatted_info_about_candidate, {'Кандидат': 'имя - Илья, возраст - 25, город - Москва'}) def test_resume(self): """Тест функции resume(). Тип возвращаемых данных верен?""" type_of_return = resume.resume() self.assertIsInstance(type_of_return, str) if __name__ == '__main__': unittest.main()
Добавим тест test_resume(), в котором будем проверять тип, возвращаемых функцией resume(), данных. Для этого воспользуемся методом assertIsInstance, который как раз это проверят (isinstance(a,b)).
Теперь запустим тест и посмотрим на результат.
Terminal
$ python -m unittest .Давайте познакомимся Как вас зовут? Борис Приятно познакомиться, Борис, сколько вам лет?: 31 И последни вопрос, откуда вы?: Владимир Спасибо за беседу, передал информацию о вашей кандидатуре руководству . ---------------------------------------------------------------------- Ran 2 tests in 26.611s OK
Как видно, при запуске теста для программы, в которую не переданы данные по умолчанию, но которая предполагает обработку данных, эти данные все равно будут собраны. Выполнение программы попросту запустится внутри теста и также будут запрошены данные от пользователя.
Оба теста возвращают OK, таким образом мы успешно проверили две взаимосвязанные программы по двум разным условиям, при этом обе проверки пишутся в две строки.
Но нам ведь необязательно останавливаться на двух проверках, количество проверок не должно ровняться количеству проверяемых функций. Давайте добавим еще тестов.
tests/test_resume.py
import unittest from examples.unittest import resume class ResumeTests(unittest.TestCase): """Тесты для программы resume.py""" def test_candidate_resume(self): """Тест функции candidate(). Возвращаемые данные верны?""" print('Функция test_candidate_resume()') formatted_info_about_candidate = resume.candidate('имя - Илья, возраст - 25, город - Москва') self.assertEqual(formatted_info_about_candidate, {'Кандидат': 'имя - Илья, возраст - 25, город - Москва'}) def test_resume(self): """Тест функции resume(). Тип возвращаемых данных верен?""" print('n', 'n', 'Функция test_resume()') type_of_return = resume.resume() self.assertIsInstance(type_of_return, str) def test_resume_nodata(self): """Тест функции main(). Случай, когда данные не переданы.""" print('n', 'n', 'Функция test_resume_nodata()') nodata_send = resume.resume() self.assertIsNotNone(nodata_send) def test_main(self): """Тест функции main(). Функция содержит данные?""" print('n', 'n', 'Функция test_main()') main_result = resume.main() self.assertTrue(main_result) if __name__ == '__main__': unittest.main()
Добавим тест test_resume_nodata(), в котором рассмотрим ситуацию, когда ответов на вопросы бот не последует. Метод assertIsNotNone проверяет, содержит ли переданный аргумент хоть какие-нибудь данные. И тест test_main() где будем использовать метод assertTrue, метод также принимает один аргумент и положительным результатом проверки будет ситуация, когда аргумент возвращает True. Напомню, что в случае данных True будет возвращено при их наличии.
И также добавим в каждый тест print() с названием теста и пробелами для наглядности.
Terminal
$ python -m unittest Функция test_candidate_resume() . Функция test_main() Давайте познакомимся Как вас зовут? Андрей Приятно познакомиться, Андрей, сколько вам лет?: 33 И последни вопрос, откуда вы?: Орел Спасибо за беседу, передал информацию о вашей кандидатуре руководству {'Кандидат': 'имя - Андрей, возраст - 33, город - Орел'} F Функция test_resume() Давайте познакомимся Как вас зовут? Вова Приятно познакомиться, Вова, сколько вам лет?: 56 И последни вопрос, откуда вы?: Минск Спасибо за беседу, передал информацию о вашей кандидатуре руководству . Функция test_resume_nodata() Давайте познакомимся Как вас зовут? Приятно познакомиться, , сколько вам лет?: И последни вопрос, откуда вы?: Спасибо за беседу, передал информацию о вашей кандидатуре руководству . ====================================================================== FAIL: test_main (test_resume.ResumeTests) Тест функции main(). Функция содержит данные? ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/tsarkoilya/kavo/TestsKAVO/tests/test_resume.py", line 30, in test_main self.assertTrue(main_result) AssertionError: None is not true ---------------------------------------------------------------------- Ran 4 tests in 34.768s FAILED (failures=1)
Все проверки, кроме test_main(), прошли успешно. И действительно функция main() только печатает результат, но ничего не возвращает.
Результат ошибочного теста ожидаемо богаче на информацию. Тут мы можем почитать информацию об ошибке и комментарий к проверке, в данном случае: None is not true.
На примере resume.py мы познакомились с вами с синтаксисом тестов, с несколькими методами проверки и познакомились с интерфейсом работы тестов.
Подробнее о TestCase
Для знакомства с некоторыми остальными возможностями unittest давайте напишем еще одну программу.
resume_evolve.py
class Resume: """Класс для беседы и сбора информации о кандидате""" list_of_questions = [ 'Как вас зовут? ', 'Сколько вам лет? ', 'Откуда вы? ', 'Какое у вас образование? ' ] def __init__(self, candidate): """Инициализация каждого кандидата""" self.candidate = candidate def preparation(self, list_of_questions): """ Сценарий беседы с кандидатом. Кандидат может согласиться начать диалог или отказаться. В случае согласия начать беседу кандидату задаются вопросы из списка list_of_questions. """ print('Здравствуйте, вы откликнулись на вакансию нашей компании', 'n', 'n', 'Желаете немного побеседовать?', sep='') option = input('Введите 'ДА' или 'НЕТ' ') if option.upper() == 'НЕТ': print('Понял вас, всего доброго!') if option.upper() == 'ДА': answers = [] print('Хорошо, тогда ответьте на несколько вопросов') for question in list_of_questions: answer = input(question) answers.append(question + ' - ' + answer) new_candidate = {self.candidate: answers} return new_candidate else: print('Неизвестная команда') def main(): ex_1 = Resume('Кандидат 1') print(ex_1.preparation(list_of_questions=Resume.list_of_questions)) if __name__ == '__main__': main()
Напишем класс Resume. В нем немного модернизируем программу из первого раздела, заодно посмотрим есть ли какая-нибудь разница между тестированием функций и классов. Экземпляром будет являться каждый новый кандидат, у которого будет выбор начинать беседу или нет и в случае согласия будем собирать ответы на интересующие нас вопросы. Для вопросов создадим отдельный список на уровне класса, такой подход удобен для масштабирования пула вопросов.
Напишем первый тест.
tests/test_resume_evolve.py
import unittest from examples.unittest import resume_evolve class ResumeEvolve(unittest.TestCase): """Тесты класса Resume""" def test_resume(self): """Тест класса Resume. Возвращаемый тип данных не является словарем?""" ex = resume_evolve.Resume('Кандидат 1') self.assertNotIsInstance(ex, dict) if __name__ == '__main__': unittest.main()
Тестом test_resume будем проверять, что экземпляр класса не словарь методом assertNotIsInstance.
Terminal
$ python -m unittest test_resume_evolve.py . ---------------------------------------------------------------------- Ran 1 test in 0.000s OK
И, конечно, это так. Тип экземпляра класса — сам этот класс, экземпляром которого он является. Но обратить внимание тут я хотел на другое, в директории tests у нас находятся два файла с тестами и запустив проверку так, как мы делали выше мы начнем исполнение обоих файлов, для того чтобы этого избежать достаточно через пробел написать название файла, который мы хотим запустить.
Добавим еще один тест.
tests/test_resume_evolve.py
import unittest from examples.unittest import resume_evolve class ResumeEvolve(unittest.TestCase): """Тесты класса Resume""" def test_resume(self): """Тест класса Resume. Возвращаемый тип данных не является словарем?""" print('n', 'Тест test_preparation()') ex = resume_evolve.Resume('Кандидат 1') self.assertNotIsInstance(ex, dict) def test_preparation_with_data(self): """Тест функции preparation(). Возвращаемый тип данных не является словарем?""" print('n', 'Тест test_preparation_with_data()') ex = resume_evolve.Resume('Кандидат 1').preparation(list_of_questions=resume_evolve.Resume.list_of_questions) self.assertNotIsInstance(ex, dict) if __name__ == '__main__': unittest.main()
Будем делать точно такую же проверку, но конкретно для функции preparation().
Terminal
$ python -m unittest test_resume_evolve.py Тест test_preparation_with_data() Здравствуйте, вы откликнулись на вакансию нашей компании Желаете немного побеседовать? Введите 'ДА' или 'НЕТ' да Хорошо, тогда ответьте на несколько вопросов Как вас зовут? Илья Сколько вам лет? 24 Откуда вы? Москва Какое у вас образование? Высшее F Тест test_preparation() . ====================================================================== FAIL: test_preparation_with_data (test_resume_evolve.ResumeEvolve) Тест функции preparation(). Возвращаемый тип данных не является словарем? ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/tsarkoilya/kavo/TestsKAVO/tests/test_resume_evolve.py", line 18, in test_preparation_with_data self.assertNotIsInstance(ex, dict) AssertionError: {'Кандидат 1': ['Как вас зовут? - Илья', 'Сколько вам лет? - 24', 'Откуда вы? - Москва', 'Какое у вас образование? - Высшее']} is an instance of <class 'dict'> ---------------------------------------------------------------------- Ran 2 tests in 8.292s FAILED (failures=1)
Тут же при исполнении теста мы можем посмотреть на работу программы, как видно результат проверки функции preparation() — ошибка. Пусть функция preparation() и собирает информацию об экземпляре класса, но конкретно эта функция возвращает словарь, о чем также написано в информации об ошибке.
Но при тестировании функции preparation() возникает еще одна ситуация, которую хотелось бы учесть при проверке. Кандидат может написать что-нибудь отличное от ДА, возникает вопрос: есть ли возможность в unittest учесть такой сценарий и как-то его обработать?
tests/test_resume_evolve.py
import unittest from examples.unittest import resume_evolve class ResumeEvolve(unittest.TestCase): """Тесты класса Resume""" def test_resume(self): """Тест класса Resume. Возвращаемый тип данных не является словарем?""" print('n', 'Тест test_preparation()') ex = resume_evolve.Resume('Кандидат 1') self.assertNotIsInstance(ex, dict) def test_preparation_with_data(self): """Тест функции preparation(). Возвращаемый тип данных не является словарем?""" print('n', 'Тест test_preparation_with_data()') ex = resume_evolve.Resume('Кандидат 1').preparation(list_of_questions=resume_evolve.Resume.list_of_questions) if ex: self.assertNotIsInstance(ex, dict) else: self.fail('Кандидат не написал ДА') if __name__ == '__main__': unittest.main()
Для этих целей unittest имеет метод fail. В качестве аргумента fail принимает сообщение, которое мы увидим при срабатывании этого метода. Такой подход добавит точности, ведь когда условие для начала беседы не соблюдены, то и ошибка говорящая, что результат является словарем не будет соответствовать действительности. Таким образом в случае, когда переменная ex содержит результат мы обрабатываем его методом assertNotIsInstance, в противном случае просто сообщаем, что клиент не захотел отвечать на вопросы.
Terminal
$ python -m unittest test_resume_evolve.py Тест test_preparation_with_data() Здравствуйте, вы откликнулись на вакансию нашей компании Желаете немного побеседовать? Введите 'ДА' или 'НЕТ' нет Понял вас, всего доброго! Неизвестная команда F Тест test_preparation() . ====================================================================== FAIL: test_preparation_with_data (test_resume_evolve.ResumeEvolve) Тест функции preparation(). Возвращаемый тип данных не является словарем? ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/tsarkoilya/kavo/TestsKAVO/tests/test_resume_evolve.py", line 21, in test_preparation_with_data self.fail('Кандидат не написал ДА') AssertionError: Кандидат не написал ДА ---------------------------------------------------------------------- Ran 2 tests in 1.778s FAILED (failures=1)
Проверка работает.
Но если мы не хотим получать ошибку, а просто пропустить тест в случа, если не было введено ДА, то можем воспользоваться еще одним методом — skipTest.
tests/test_resume_evolve.py
... ex = resume_evolve.Resume('Кандидат 1').preparation(list_of_questions=resume_evolve.Resume.list_of_questions) if ex: self.assertNotIsInstance(ex, dict) else: self.skipTest('Кандидат не написал ДА') ...
Terminal
... Желаете немного побеседовать? Введите 'ДА' или 'НЕТ' нет Понял вас, всего доброго! Неизвестная команда s Тест test_preparation() . ---------------------------------------------------------------------- Ran 2 tests in 2.118s OK (skipped=1)
При использовании skipTest мы увидим s, а в итоговом отчете информацию о количестве пропущенных текстов.
Еще один момент, который мы можем улучшить это описание теста. Сейчас мы с помощью print() сами пишем какой тест в данный момент отрабатывает, но unittest предоставляет свои методы для этого.
tests/test_resume_evolve.py
import unittest from examples.unittest import resume_evolve class ResumeEvolve(unittest.TestCase): """Тесты класса Resume""" def test_resume(self): """Тест класса Resume. Возвращаемый тип данных не является словарем?""" print('n', self.shortDescription(), 'n', self.id(), sep='') ex = resume_evolve.Resume('Кандидат 1') self.assertNotIsInstance(ex, dict) def test_preparation_with_data(self): """Тест функции preparation(). Возвращаемый тип данных не является словарем?""" print('n', self.shortDescription(), 'n', self.id(), sep='') ex = resume_evolve.Resume('Кандидат 1').preparation(list_of_questions=resume_evolve.Resume.list_of_questions) if ex: self.assertNotIsInstance(ex, dict) else: self.fail('Кандидат не написал ДА') if __name__ == '__main__': unittest.main()
Первый метод shortDescription(), который будет выводить первую строку docstring’а теста, это то, что мы пишем в тройных кавычках под объявлением метода. Метод id() будет возвращать имя теста, это как раз то, что ранее мы писали вручную.
Запустим тестирование теперь.
Terminal
$ python -m unittest test_resume_evolve.py Тест функции preparation(). Возвращаемый тип данных не является словарем? test_resume_evolve.ResumeEvolve.test_preparation_with_data Здравствуйте, вы откликнулись на вакансию нашей компании Желаете немного побеседовать? Введите 'ДА' или 'НЕТ' нет Понял вас, всего доброго! Неизвестная команда F Тест класса Resume. Возвращаемый тип данных не является словарем? test_resume_evolve.ResumeEvolve.test_resume . ====================================================================== FAIL: test_preparation_with_data (test_resume_evolve.ResumeEvolve) Тест функции preparation(). Возвращаемый тип данных не является словарем? ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/tsarkoilya/kavo/TestsKAVO/tests/test_resume_evolve.py", line 21, in test_preparation_with_data self.fail('Кандидат не написал ДА') AssertionError: Кандидат не написал ДА ---------------------------------------------------------------------- Ran 2 tests in 4.058s FAILED (failures=1)
Обратите внимание на текст перед тестами, первая строка описание из docstring’а, вторая имя теста вида: название файла.название класса. название теста. Достаточно удобно и понятно, и, конечно, любые дополнительные комментарии модно добавить к этому описанию.
Еще два особых метода, о которых хотелось бы упомянуть — setUp() и tearDown() и их аналоги уровня класса setUpClass() и tearDownClass().
Метод setUp() вызывается перед запуском каждого теста, метод setUpClass() — перед запуском тестов класса.
Методы tearDown() и tearDownClass() вызываются при завершении теста.
tests/test_resume_evolve.py
import unittest from examples.unittest import resume_evolve class ResumeEvolve(unittest.TestCase): """Тесты класса Resume""" @classmethod def setUpClass(cls): """Метод, который срабатывает перед запуском всех тестов класса.""" print('Запускаем тесты класса ResumeEvolve') @classmethod def tearDownClass(cls): """Метод, который срабатывает после отработки всех тестов класса.""" print('n', 'n', 'Закончили тесты класса ResumeEvolve', sep='') def setUp(self): """Метод, который срабатывает перед запуском каждого теста.""" print('n', self.shortDescription(), 'n', self.id(), sep='') def tearDown(self): """Метод, который срабатывает после отработки каждого теста.""" print(f'Тест: {self.id()} закончен') def test_resume(self): """Тест класса Resume. Возвращаемый тип данных не является словарем?""" ex = resume_evolve.Resume('Кандидат 1') self.assertNotIsInstance(ex, dict) def test_preparation_with_data(self): """Тест функции preparation(). Возвращаемый тип данных не является словарем?""" ex = resume_evolve.Resume('Кандидат 1').preparation(list_of_questions=resume_evolve.Resume.list_of_questions) if ex: self.assertNotIsInstance(ex, dict) else: self.skipTest('Кандидат не написал ДА') if __name__ == '__main__': unittest.main()
Методы setUpClass() и tearDownClass() являются методами класса, поэтому для их использования необходимы соответствующие декораторы. В setup метода давайте выводит сообщение, что тесты класса начата, а в teardown — что закончены.
Методы setUp() и tearDown() работаю для каждого теста, поэтому в них мы можем вынести дублирующийся код для описания.
Terminal
$ python -m unittest test_resume_evolve.py Запускаем тесты класса ResumeEvolve Тест функции preparation(). Возвращаемый тип данных не является словарем? test_resume_evolve.ResumeEvolve.test_preparation_with_data Здравствуйте, вы откликнулись на вакансию нашей компании Желаете немного побеседовать? Введите 'ДА' или 'НЕТ' нет Понял вас, всего доброго! Неизвестная команда Тест: test_resume_evolve.ResumeEvolve.test_preparation_with_data закончен s Тест класса Resume. Возвращаемый тип данных не является словарем? test_resume_evolve.ResumeEvolve.test_resume Тест: test_resume_evolve.ResumeEvolve.test_resume закончен . Закончили тесты класса ResumeEvolve ---------------------------------------------------------------------- Ran 2 tests in 3.285s OK (skipped=1)
Теперь все, что касается описания процесса тестирования оформлено достаточно компактно и элегантно. Представьте сколько строчек мы экономим этими методами, если класс содержит не 2 теста, а значительно более и насколько прозе нам вносить изменения в описание при необходимости.
И, конечно, эти методы могут использоваться не только под описание, а например под создание каких-то общих переменных, используемых в каждом тесте.
asserts
Мы познакомились с вами с некоторыми наиболее частыми assert’ами, в официальной документации все assert’ы разбиты на несколько таблиц, первая из них — наиболее частые, ознакомится с таблицами вы можете в этой документации, мы же рассмотрим их на примерах.
tests/all_asserts.py
import unittest class AllMostCommonlyUsedAssertsExamples(unittest.TestCase): """В данном класс рассмотрим все наиболее часто используемые assert методы""" def test_assert_equal(self): """Метод assertEqual(x, y) (X == Y)""" self.assertEqual(10, 10) def test_assert_not_equal(self): """Метод assertNotEqual(x, y) (X != Y)""" self.assertNotEqual([1, 2, 3], [2, 3, 4]) def test_assert_true(self): """Метод assertTrue(x) (bool(x) is True)""" self.assertTrue({'Russia': 'Moscow'}) def test_assert_false(self): """Метод assertFalse(x) (bool(x) is False)""" self.assertFalse({}) def test_assert_none(self): """Метод assertIsNone(x) (x is None)""" def test(a=10, b=5): result = a * b self.assertIsNone(test()) def test_assert_is_not_none(self): """Метод assertIsNotNone(x) (x is not None)""" self.assertIsNotNone('Это не None') def test_assert_in(self): """Метод assertIn(x, y) (x in y)""" example = 'python' self.assertIn(example, ['C#', 'python', 'C++']) def test_assert_not_in(self): """Метод assertNotIn(x, y) (x not in y)""" example = 'python' self.assertNotIn(example, ['C#', 'PHP', 'C++']) def test_assert_is(self): """Метод assertIs(x, y) (x is y)""" ex = (1,) self.assertIs(type(ex), tuple) def test_assert_is_not(self): """Метод assertIsNot(x, y) (x is not y)""" ex = (1, 2, 3) self.assertIsNot(type(ex), set) def test_assert_is_instance(self): """Метод assertIsInstance(x, y) (isinstance(x, y))""" class Test: pass ex = Test() self.assertIsInstance(ex, Test) def test_assert_not_is_instance(self): """Метод assertNotIsInstance(x, y) (not isinstance(x, y))""" self.assertNotIsInstance(type(2), type('2')) if __name__ == '__main__': unittest.main()
В классе AllMostCommonlyUsedAssertsExamples приведены примеры всех наиболее часто используемых assert’ов. Каких-то дополнительных комментариев об этих методах дать не получится, их работа максимально понятна и очевидна.
Terminal
$ python -m unittest all_asserts.py ............ ---------------------------------------------------------------------- Ran 12 tests in 0.001s OK
Ранее я не упоминал об этом, но все тесты имею один необязательный параметр — msg. В этом параметре мы можем написать сообщение, которое мы увидим в случае ошибочного теста.
tests/all_asserts.py
... def test_assert_none(self): """Метод assertIsNone(x) (x is None)""" def test(a=10, b=5): result = a * b return result self.assertIsNone(test(), 'Функция test возвращает результат отличный от None') ...
Terminal
$ python -m unittest all_asserts.py .......F.... ====================================================================== FAIL: test_assert_none (all_asserts.AllMostCommonlyUsedAssertsExamples) Метод assertIsNone(x) (x is None) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/tsarkoilya/kavo/TestsKAVO/tests/all_asserts.py", line 30, in test_assert_none self.assertIsNone(test(), 'Функция test возвращает результат отличный от None') AssertionError: 50 is not None : Функция test возвращает результат отличный от None ---------------------------------------------------------------------- Ran 12 tests in 0.001s FAILED (failures=1)
Например давайте сделаем, чтоб функция test теста test_assert_none возвращала результат, и добавим параметр msg в метод assertIsNone. Сообщение об ошибке мы видим вместо сообщения об ошибке, которое генерируется по умолчанию, если параметр msg не задан.
Следующая группа проверок, которая вынесена в отдельную таблицу — проверка создания исключений, предупреждений и сообщений журнала. Для подобных проверок unittest предоставляет 6 методов, будем разбирать по 2.
tests/all_asserts.py
... class ExceptionsWarningslogsAssertsExamples(unittest.TestCase): """Проверка создания исключений, предупреждений и сообщений журнала""" def setUp(self): print('n', self.shortDescription(), sep='') def test_assert_raises(self): """Метод assertRaises (Exception, Fragment of Code)""" with self.assertRaises(IndexError) as exc: lst = [1, 2, 3] print(lst[5]) print(exc) print(exc.exception) # def test(): # lst = [1, 2, 3] # return lst[5] # # self.assertRaises(IndexError, test) def test_assert_raises_regex(self): """Метод assertRaisesRegex (Exception, Error as Str, Fragment of Code)""" error = 'list index out of range' with self.assertRaisesRegex(IndexError, error) as exc: lst = [1, 2, 3] print(lst[5]) print(exc) print(exc.exception) # def test(): # lst = [1, 2, 3] # return lst[5] # # error = 'list index out of range' # self.assertRaisesRegex(IndexError, error, test) if __name__ == '__main__': unittest.main()
Первыми двумя методами будут — assertRaises и assertRaisesRegex. Оба метода проверяют результат какого-нибудь фрагмента кода на соответствие ошибки. Разница в том, что assertRaisesRegex ожидает получить в качестве аргумента еще и текст ошибки, для этого метод assertRaisesRegex поддерживает использование регулярных выражений для описания текста ошибки.
Рассмотрим каждый тест отдельно.
test_assert_raises. Фрагмент кода сверху и фрагмент кода в комментарии отрабатывают одинаково и оба возвращают ОК. Но во втором случае тест описан классическим способом, к которому мы уже привыкли, то есть сверху мы написали функцию, которая будет возвращать ошибку, а ниже в методе assertRaises мы первым параметром передали предполагаемый тип ошибки, а вторым проверяемый фрагмент кода.
Но сами разработчики модуля unittest рекомендует для этих методов использовать менеджер контекста with, поскольку таким образом мы делаем ту же самую проверку, что и в нижнем примере, но при этом мы сразу сохраняем исключение в переменную. С этой переменной мы далее при необходимости можем удобно взаимодействовать, например, можем распечатать текст исключения ошибки. И сама проверка при использовании with получается лаконичнее.
test_assert_raises_regex. Точно такая же проверка как и в первом, но дополнительным параметром для успешной проверки используется предполагаемый текст с сообщением самой ошибки.
Terminal
$ python -m unittest all_asserts.ExceptionsWarningslogsAssertsExamples Метод assertRaises (Exception, Fragment of Code) <unittest.case._AssertRaisesContext object at 0x7f9933023dc0> list index out of range . Метод assertRaisesRegex (Exception, Error as Str, Fragment of Code) <unittest.case._AssertRaisesContext object at 0x7f9933023be0> list index out of range . ---------------------------------------------------------------------- Ran 2 tests in 0.000s OK
Теперь запустим тесты. Для запуска только одного класса из файла используйте запись — название_файла.название_класса. Как видно тесты отработали успешно и в каждом мы видим вывод двух print(), первый, куда мы передали exc, распечатывает описание объекта, а второй, куда мы передали exc.exception — описание ошибки из этого объекта.
Предупреждения (Warnings), почти то же самое что и ошибки, но предупреждения в отличие от ошибок не прерывают выполнение программы, весь список предупреждений я приводил на схеме в блоке по ООП в python в разделе, где речь шла о try/except. Текст предупреждений мы можем писать самостоятельно, используя доступные типы предупреждений. Посмотрим как это выглядит.
tests/all_asserts.py
... class ExceptionsWarningslogsAssertsExamples(unittest.TestCase): """Проверка создания исключений, предупреждений и сообщений журнала""" def setUp(self): print('n', self.shortDescription(), sep='') ... def test_assert_warns(self): """Метод assertWarns (Warning, Fragment of Code)""" with self.assertWarns(SyntaxWarning) as wrng: def syntax_warning(): version_python = "3.8" result = version_python is "3.8" warnings.warn('Для сравнения лучше использовать == ', SyntaxWarning) return result syntax_warning() print(wrng) print(wrng.warnings) print(wrng.warning) print(wrng.lineno) print(wrng.filename) # def syntax_warning(): # version_python = "3.8" # result = version_python is "3.8" # warnings.warn('Для сравнения лучше использовать == ', SyntaxWarning) # return result # # self.assertWarns(SyntaxWarning, syntax_warning) def test_assert_warns_regex(self): """Метод assertWarnsRegex (Warning, Warning as Str, Fragment of Code)""" with self.assertWarnsRegex(SyntaxWarning, 'Для сравнения лучше использовать == ') as wrng: def syntax_warning(): version_python = "3.8" result = version_python is "3.8" warnings.warn('Для сравнения лучше использовать == ', SyntaxWarning) return result syntax_warning() print(wrng) print(wrng.warnings) print(wrng.warning) print(wrng.lineno) print(wrng.filename) # def syntax_warning(): # version_python = "3.8" # result = version_python is "3.8" # warnings.warn('Для сравнения лучше использовать == ', SyntaxWarning) # return result # # warning = 'Для сравнения лучше использовать == ' # self.assertWarnsRegex(SyntaxWarning, warning, syntax_warning) if __name__ == '__main__': unittest.main()
Методы assertWarns и assertWarnsRegex. Логика точно такая же как в случае assertRaises и assertRaisesRegex, но работаем мы уже с предупреждениями. Также есть два варианта вызова, стандартно (тесты в комментариях) и через with.
Посмотрим на test_assert_warns. В менеджере with вызываем метод assertWarns и передаем в него SyntaxWarning, таким образом код внутри контекста будет проверяться на вызов данного предупреждения. Напишем функцию syntax_warning() где для сравнения будем использовать is, вместо ==, конечно, использование == предпочтительнее, но и с использованием is программа успешно отработает и вернет True. Но все-таки нужно стремиться писать код правильно, поэтому добавим в программу вызов предупреждения типа SyntaxWarning с текстом, говорящим, что предпочтительнее здесь использовать ==.
Пример максимально простой, но думаю хорошо демонстрирующий работу данного метода проверки.
С test_assert_warns_regex ситуация такая же, но для работы необходим дополнительный параметр с текстом предупреждения.
У переменной хранящей предупреждение есть 4 вызываемых параметра, посмотрим на них при запуске тестов.
Terminal
$ python -m unittest all_asserts.ExceptionsWarningslogsAssertsExamples ... Метод assertWarns (Warning, Fragment of Code) <unittest.case._AssertWarnsContext object at 0x7fa000ea0130> [<warnings.WarningMessage object at 0x7fa000ea0250>] Для сравнения лучше использовать == 119 /home/tsarkoilya/kavo/TestsKAVO/tests/all_asserts.py . Метод assertWarnsRegex (Warning, Warning as Str, Fragment of Code) <unittest.case._AssertWarnsContext object at 0x7fa000e43f70> [<warnings.WarningMessage object at 0x7fa000ea0220>] Для сравнения лучше использовать == 145 /home/tsarkoilya/kavo/TestsKAVO/tests/all_asserts.py . ---------------------------------------------------------------------- Ran 4 tests in 0.001s OK
Отработку тестов test_assert_raises и test_assert_raises_regex рассмотренных выше уберем из результата. Методы успешно отработали. Посмотри на 4 вызываемых параметра.
.warnings объект хранящий сообщения
.warning само сообщения предупреждения
.lineno строка, в которой вызывается предупреждение
.filename название файла.
Осталось еще 2 метода из этой категории.
tests/all_asserts.py
... class ExceptionsWarningslogsAssertsExamples(unittest.TestCase): """Проверка создания исключений, предупреждений и сообщений журнала""" def setUp(self): print('n', self.shortDescription(), sep='') ... def test_assert_logs(self): """Метод assertLogs (is logging.Logger obj)""" with self.assertLogs('Journal', level=None) as lg: logging.getLogger('Journal').info('Example Message') logging.getLogger('Journal.warnings').warning('Example Warning') logging.getLogger('Journal.errors').error('Example Error') print(lg.output) print(lg.records) # ex = logging.Logger('Journal') # ex.log(20, 'Example message fo level INFO') # self.assertLogs(ex) def test_assert_no_logs(self): """Метод assertNoLogs (logging.Logger obj is False)""" with self.assertNoLogs('Journal', level='INFO') as lg: logging.getLogger('Journal').info('Example Message') print(lg.output) print(lg.records) # ex = logging.Logger('Journal') # self.assertNoLogs(ex) if __name__ == '__main__': unittest.main()
Методы assertLogs и assertNoLogs. Используются для проверки наличия записей в определенных уровнях журнала логов. Метод assertLogs проверяет, что записи на выбранных уровнях, либо в журнале существуют, метод assertNoLogs используется для обратной проверки.
Рассмотрим примеры, чтобы лучше понять происходящее.
test_assert_logs. В этом тесте используем метод assertLogs, первый параметр имя журнала, второй — уровень, наличие сообщений на котором мы будем проверять. Передадим None, таким образом мы будем искать записи на любом уровне. Уровни этого журнала записываются либо строковым значением, либо числовым. Так, например, уровень INFO соответствует значению 20, а уровень WARNING значение 30.
Внутри test_assert_logs мы добавляем записи в журнал на уровни INFO, WARNING и ERROR, таким образом журнал содержит 3 записи и метод assertLogs успешно отрабатывает.
В закомментированном варианте ситуация аналогичная.
test_assert_no_logs. Проверяет, что весь журнал, либо отдельный уровень не содержит записи. В данном примере будем проверять уровень INFO и передадим в этот уровень одну запись, таким образом метод должен отработать с ошибкой, так и будет.
В закомментированном варианте создадим пустой журнал логов, такая проверка отработает успешно.
Посмотрим на результат.
Terminal
$ python -m unittest all_asserts.ExceptionsWarningslogsAssertsExamples ... Метод assertLogs (is logging.Logger obj) ['INFO:Journal:Example Message', 'WARNING:Journal.warnings:Example Warning', 'ERROR:Journal.errors:Example Error'] [<LogRecord: Journal, 20, /home/tsarkoilya/kavo/TestsKAVO/tests/all_asserts.py, 169, "Example Message">, <LogRecord: Journal.warnings, 30, /home/tsarkoilya/kavo/TestsKAVO/tests/all_asserts.py, 170, "Example Warning">, <LogRecord: Journal.errors, 40, /home/tsarkoilya/kavo/TestsKAVO/tests/all_asserts.py, 171, "Example Error">] . Метод assertNoLogs (logging.Logger obj is False) F ====================================================================== FAIL: test_assert_no_logs (all_asserts.ExceptionsWarningslogsAssertsExamples) Метод assertNoLogs (logging.Logger obj is False) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/tsarkoilya/kavo/TestsKAVO/tests/all_asserts.py", line 183, in test_assert_no_logs with self.assertNoLogs('Journal', level='INFO') as lg: File "/usr/lib/python3.10/unittest/_log.py", line 75, in __exit__ self._raiseFailure( AssertionError: Unexpected logs found: ['INFO:Journal:Example Message'] ---------------------------------------------------------------------- Ran 6 tests in 0.001s FAILED (failures=1)
Тест test_assert_logs отрабатывает успешно. И мы видим результат двух параметров.
.output возвращает содержимое журнала, те самые 3 записи, которые мы добавили вручную.
.records возвращает историю добавления записей в журнал, первый параметр заголовок записи, второй уровень в численном эквиваленте, третий — текст записи.
Тест test_assert_no_logs ожидаемо отрабатывает с ошибкой, в которой написано, что записи найдены.
tests/all_asserts.py
... def test_assert_no_logs(self): """Метод assertNoLogs (logging.Logger obj is False)""" with self.assertNoLogs('Journal', level='ERROR') as lg: logging.getLogger('Journal').info('Example Message') # ex = logging.Logger('Journal') # self.assertNoLogs(ex) ...
Terminal
... Метод assertNoLogs (logging.Logger obj is False) . ---------------------------------------------------------------------- Ran 1 tests in 0.002s OK
А вот если мы заменим уровень INFO на, например, ERROR, то тест отработает успешно, потому что на этот уровень записи мы не добавляли. .output и .records в таком случае придется убрать, потому что lg содержит пустой Logger().
Теперь перейдем к следующей категории.
tests/all_asserts.py
... class MoreSpecificChecksAssertsExamples(unittest.TestCase): """Методы, используемые для более специфических проверок""" def setUp(self): print('n', self.shortDescription(), sep='') def test_assert_almost_equal(self): """Метод assertAlmostEqual(X, Y, знаков после запятой) (X == Y)""" self.assertAlmostEqual(10.001, 10.002, 2) def test_assert_not_almost_equal(self): """Метод assertNotAlmostEqual(X, Y, знаков после запятой) (X != Y)""" self.assertNotAlmostEqual(10.001, 10.002, 3) def test_assert_greater(self): """Метод assertGreater(X, Y) (X > Y)""" self.assertGreater([1, 2, 3], [1, 2]) def test_assert_greater_equal(self): """Метод assertGreaterEqual(X, Y) (X >= Y)""" self.assertGreaterEqual('первый', 'второй') def test_assert_less(self): """Метод assertLess(X, Y) (X < Y)""" self.assertLess('три', 'четыре') def test_assert_less_equal(self): """Метод assertLessEqual(X, Y) (X <= Y)""" self.assertLessEqual('', '') def test_assert_regex(self): """Метод assertRegex(Text, Liter) (Liter in Text)""" self.assertRegex('один два три', ' ') def test_assert_not_regex(self): """Метод assertNotRegex(Text, Liter) (Liter not in Text)""" self.assertNotRegex('один два три', 'одиндва') def test_assert_count_equal(self): """Метод assertCountEqual(X, Y) (Элементы X == Y, порядок значений не важен)""" self.assertCountEqual(['один', 2, 3], [3, 'один', 2]) if __name__ == '__main__': unittest.main()
Terminal
$ python -m unittest all_asserts.MoreSpecificChecksAssertsExamples Метод assertAlmostEqual(X, Y, знаков после запятой) (X == Y) . Метод assertCountEqual(X, Y) (Элементы X == Y, порядок значений не важен) . Метод assertGreater(X, Y) (X > Y) . Метод assertGreaterEqual(X, Y) (X >= Y) . Метод assertLess(X, Y) (X < Y) . Метод assertLessEqual(X, Y) (X <= Y) . Метод assertNotAlmostEqual(X, Y, знаков после запятой) (X != Y) . Метод assertNotRegex(Text, Liter) (Liter not in Text) . Метод assertRegex(Text, Liter) (Liter in Text) . ---------------------------------------------------------------------- Ran 9 tests in 0.001s OK
Категория — наиболее специфические проверки. С этими проверками все гораздо проще, чем с предыдущими, для понимания их работы достаточно ознакомиться с примерами и комментариями к ним.
Последняя категория — методы сравнения разных типов данных. Разработчики предупреждают, что использование этих методов обычно не имеет смысла, поскольку все они встроены в assertEqual.
tests/all_asserts.py
... class TypeSpecificAssertsExamples(unittest.TestCase): """Список методов, зависящих от типа, автоматически используемых в assertEqual()""" def setUp(self): print('n', self.shortDescription(), sep='') def test_assert_multiline_equal(self): """Метод assertMultiLineEqual(str(X), str(Y)) (str(X) == str(Y))""" self.assertMultiLineEqual('пример', 'пример') def test_assert_list_equal(self): """Метод assertListEqual(list(X), list(Y)) (list(X) == list(Y))""" self.assertListEqual(['пример', 'пример'], ['пример', 'пример']) def test_assert_tuple_equal(self): """Метод assertTupleEqual(tuple(X), tuple(Y)) (tuple(X) == tuple(Y))""" self.assertTupleEqual(('пример', 'пример'), ('пример', 'пример')) def test_assert_set_equal(self): """Метод assertSetEqual(set(X), set(Y)) (set(X) == set(Y))""" self.assertSetEqual({'пример', 'пример'}, {'пример', 'пример'}) def test_assert_dict_equal(self): """Метод assertDictEqual(dict(X), dict(Y)) (dict(X) == dict(Y))""" self.assertDictEqual({'пример': 'пример'}, {'пример': 'пример'}) def test_assert_sequence_equal(self): """Метод assertSequenceEqual(X, Y, тип=None) (X == Y, можно выбрать тип)""" self.assertMultiLineEqual('строка', 'строка') if __name__ == '__main__': unittest.main()
Terminal
$ python -m unittest all_asserts.TypeSpecificAssertsExamples Метод assertDictEqual(dict(X), dict(Y)) (dict(X) == dict(Y)) . Метод assertListEqual(list(X), list(Y)) (list(X) == list(Y)) . Метод assertMultiLineEqual(str(X), str(Y)) (str(X) == str(Y)) . Метод assertSequenceEqual(X, Y, тип=None) (X == Y, можно выбрать тип) . Метод assertSetEqual(set(X), set(Y)) (set(X) == set(Y)) . Метод assertTupleEqual(tuple(X), tuple(Y)) (tuple(X) == tuple(Y)) . ---------------------------------------------------------------------- Ran 6 tests in 0.000s OK
С этой категорией все тоже достаточно очевидно.
Таким образом мы посмотрели на все существующие методы сравнения.
TestSuite
В файле all_asserts.py получилось 33 теста, допустим мы хотим запускать только часть из них. TestSuite как раз используется для группировки тестов и запуска этой группы. Для знакомства создадим новый файл test_suite.py.
tests/test_suite.py
import unittest import all_asserts exTestSuite = unittest.TestSuite() exTestSuite.addTest(unittest.makeSuite(all_asserts.AllMostCommonlyUsedAssertsExamples)) if __name__ == '__main__': unittest.TextTestRunner().run(exTestSuite)
Синтаксис состоит всего из нескольких методов. Сначала создадим экземпляр TestSuite, а после добавим в него методом makeSuite один из классов файла all_asserts.py.
Запускать файл будем с помощью класса TextTestRunner и его метода run. Этот класс умеет запускать как TestSuite, так и TestCase. TextTestRunner имеет некоторые параметры, которые мы скоро увидим.
Terminal
$ python test_suite.py ............ ---------------------------------------------------------------------- Ran 12 tests in 0.000s OK
Если мы хотим увидеть какое-нибудь описание, то мы должны изменить значение параметра verbosity класса TextTestRunner.
tests/test_suite.py
import unittest import all_asserts exTestSuite = unittest.TestSuite() exTestSuite.addTest(unittest.makeSuite(all_asserts.AllMostCommonlyUsedAssertsExamples)) if __name__ == '__main__': unittest.TextTestRunner(verbosity=2).run(exTestSuite)
Terminal
$ python test_suite.py test_assert_equal (all_asserts.AllMostCommonlyUsedAssertsExamples) Метод assertEqual(x, y) (X == Y) ... ok test_assert_false (all_asserts.AllMostCommonlyUsedAssertsExamples) Метод assertFalse(x) (bool(x) is False) ... ok test_assert_in (all_asserts.AllMostCommonlyUsedAssertsExamples) Метод assertIn(x, y) (x in y) ... ok test_assert_is (all_asserts.AllMostCommonlyUsedAssertsExamples) Метод assertIs(x, y) (x is y) ... ok test_assert_is_instance (all_asserts.AllMostCommonlyUsedAssertsExamples) Метод assertIsInstance(x, y) (isinstance(x, y)) ... ok test_assert_is_not (all_asserts.AllMostCommonlyUsedAssertsExamples) Метод assertIsNot(x, y) (x is not y) ... ok test_assert_is_not_none (all_asserts.AllMostCommonlyUsedAssertsExamples) Метод assertIsNotNone(x) (x is not None) ... ok test_assert_none (all_asserts.AllMostCommonlyUsedAssertsExamples) Метод assertIsNone(x) (x is None) ... ok test_assert_not_equal (all_asserts.AllMostCommonlyUsedAssertsExamples) Метод assertNotEqual(x, y) (X != Y) ... ok test_assert_not_in (all_asserts.AllMostCommonlyUsedAssertsExamples) Метод assertNotIn(x, y) (x not in y) ... ok test_assert_not_is_instance (all_asserts.AllMostCommonlyUsedAssertsExamples) Метод assertNotIsInstance(x, y) (not isinstance(x, y)) ... ok test_assert_true (all_asserts.AllMostCommonlyUsedAssertsExamples) Метод assertTrue(x) (bool(x) is True) ... ok ---------------------------------------------------------------------- Ran 12 tests in 0.001s OK
По умолчанию параметр verbosity равен 1, для того чтобы увидеть описания тестов, необходимо заменить значение verbosity на любое значение отличное от 1.
tests/test_suite.py
import unittest import all_asserts exTestSuite = unittest.TestSuite() exTestSuite.addTest(unittest.makeSuite(all_asserts.AllMostCommonlyUsedAssertsExamples)) exTestSuite.addTests(unittest.makeSuite(all_asserts.MoreSpecificChecksAssertsExamples)) print(f'Всего тестов {exTestSuite.countTestCases()}') if __name__ == '__main__': unittest.TextTestRunner().run(exTestSuite)
Terminal
$ python test_suite.py Всего тестов 21 ............ Метод assertAlmostEqual(X, Y, знаков после запятой) (X == Y) . Метод assertCountEqual(X, Y) (Элементы X == Y, порядок значений не важен) . Метод assertGreater(X, Y) (X > Y) . Метод assertGreaterEqual(X, Y) (X >= Y) . Метод assertLess(X, Y) (X < Y) . Метод assertLessEqual(X, Y) (X <= Y) . Метод assertNotAlmostEqual(X, Y, знаков после запятой) (X != Y) . Метод assertNotRegex(Text, Liter) (Liter not in Text) . Метод assertRegex(Text, Liter) (Liter in Text) . ---------------------------------------------------------------------- Ran 21 tests in 0.001s OK
Метод countTestCases() выводит количество тестов.
Добавим к exTestSuite тесты класса MoreSpecificChecksAssertsExamples методом addTests(). Метод addTests() применяется к итерируемым объектам. Но на данный момент разница может быть непонятна, потому что мы использовали метод addTest() так же как и addTests() для целого класса. Но как нам поступить если мы хотим использовать выборочные тесты из этого класса.
tests/test_suite.py
import unittest import all_asserts exTestSuite = unittest.TestSuite() exTestSuite.addTest(all_asserts.AllMostCommonlyUsedAssertsExamples('test_assert_true')) print(f'Всего тестов {exTestSuite.countTestCases()}') if __name__ == '__main__': unittest.TextTestRunner().run(exTestSuite)
Terminal
$ python test_suite.py Всего тестов 1 . ---------------------------------------------------------------------- Ran 1 test in 0.000s OK
Для проверки единственного метода класса используется чистый addTest(), куда передается класс, а в самом классе в качестве атрибута передается имя теста в строковом виде.
TestLoader
Класс TestLoader также используется для создания наборов тестов из модулей и классов, и как говорят сами разработчики в использовании TestLoader обычно нет большого смысла, тем не познакомимся с его возможностями.
Для знакомства создадим новый файл test_loader.py.
tests/test_loader.py
import unittest import all_asserts exTestLoader = unittest.TestLoader() tests = exTestLoader.loadTestsFromTestCase(all_asserts.AllMostCommonlyUsedAssertsExamples) # loadTestsFromTestCase(). Обрабатывает все тесты класса # tests = exTestLoader.loadTestsFromModule(all_asserts) # loadTestsFromModule(). Обрабатывает все тесты файла # tests = exTestLoader.loadTestsFromName("all_asserts.AllMostCommonlyUsedAssertsExamples.test_assert_in") # loadTestsFromName(). Обрабатывает все тесты файла, класса или отдельного теста. Уровень определяется точечной нотацией tests_names = exTestLoader.getTestCaseNames(all_asserts.ExceptionsWarningslogsAssertsExamples) print(tests_names) if __name__ == '__main__': unittest.TextTestRunner().run(tests)
TestLoader предоставляет несколько возможностей для выбора тестов, все они представлены на примере, мы можем обработать все тесты файла, все тесты класса, либо любой отдельно взятый тест.
Метод getTestCaseNames сформирует список имен тестов класса.
Terminal
$ python test_loader.py ['test_assert_logs', 'test_assert_no_logs', 'test_assert_raises', 'test_assert_raises_regex', 'test_assert_warns', 'test_assert_warns_regex'] ............ ---------------------------------------------------------------------- Ran 12 tests in 0.000s OK
При запуске получаем список имен тестов класса ExceptionsWarningslogsAssertsExamples и запускаем все тесты класса AllMostCommonlyUsedAssertsExamples.
TestResultr
TestResult используется для компиляции информации о пройденных тестах.
tests/test_result.py
import unittest import all_asserts exTestLoader = unittest.TestLoader() tests = exTestLoader.loadTestsFromTestCase(all_asserts.AllMostCommonlyUsedAssertsExamples) exTestResult = unittest.TestResult() if __name__ == '__main__': exTestResult = unittest.TextTestRunner().run(tests) print('неожиданные исключения ', exTestResult.errors) print('явно сигнализированные с использованием assert* методов исключения ', exTestResult.failures) print('тесты содержащие причину пропуска ', exTestResult.skipped) print('ожидаемые ошибки ', exTestResult.expectedFailures) print('тесты, которые ожидались завершиться ошибкой, но завершились успешно ', exTestResult.unexpectedSuccesses) print('всего тестов ', exTestResult.testsRun) print('Все тесты прошли успешно?', exTestResult.wasSuccessful())
Terminal
$ python test_result.py F........... ====================================================================== FAIL: test_assert_equal (all_asserts.AllMostCommonlyUsedAssertsExamples) Метод assertEqual(x, y) (X == Y) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/tsarkoilya/kavo/TestsKAVO/tests/all_asserts.py", line 11, in test_assert_equal self.assertEqual(10, 12) AssertionError: 10 != 12 ---------------------------------------------------------------------- Ran 12 tests in 0.001s FAILED (failures=1) неожиданные исключения [] явно сигнализированные с использованием assert* методов исключения [(<all_asserts.AllMostCommonlyUsedAssertsExamples testMethod=test_assert_equal>, 'Traceback (most recent call last):n File "/home/tsarkoilya/kavo/TestsKAVO/tests/all_asserts.py", line 11, in test_assert_equaln self.assertEqual(10, 12)nAssertionError: 10 != 12n')] тесты содержащие причину пропуска [] ожидаемые ошибки [] тесты, которые ожидались завершиться ошибкой, но завершились успешно [] всего тестов 12 Все тесты прошли успешно? False
TestResult имеет различные статистические методы, которые возвращают список попавших под каждую категорию ошибок, я намеренно добавил в тесты одну ошибку, чтобы посмотреть как это выглядит. Конечно, поскольку это список, то к нему можно применить функцию len(), для вывода числового значения.
IsolatedAsyncioTestCase
unittest поддерживает асинхронное выполнение тестов, для этого вместо TestCase необходимо наследоваться от IsolatedAsyncioTestCase.
tests/iso_lated_asyncio_test_case.py
import unittest import asyncio async def async_summa(x, y): await asyncio.sleep(1) print('Спим 1...') return x + y async def async_func(): await asyncio.sleep(0.5) print('Спим 0.5..') class Test(unittest.IsolatedAsyncioTestCase): def setUp(self): """Метод, который срабатывает перед запуском каждого теста.""" print('n', self.shortDescription(), sep='') async def asyncSetUp(self): print('Метод asyncSetUp()') await asyncio.gather(async_func(), async_summa(5, 5)) async def asyncTearDown(self): print('Метод asyncTearDown()') await asyncio.gather(async_func()) def tearDown(self): """Метод, который срабатывает после отработки каждого теста.""" print(f'Тест: {self.id()} закончен') async def test_assert_equal(self): """Метод assertEqual(x, y) (X == Y)""" self.assertEqual(await async_summa(5, 5), 10) if __name__ == "__main__": unittest.main()
При наследовании IsolatedAsyncioTestCase нам доступны корутины. Также IsolatedAsyncioTestCase имеет два метода asyncSetUp и asyncTearDown. asyncSetUp вызывается после обычного SetUp, а asyncTearDown перед обычным TearDown. В данном примере напишем два корутина async_summa() и async_func(). Обе эти функции мы будем вызывать перед выполнением тестов в методе asyncSetUp, после этого async_summa() мы еще раз вызовем в test_assert_equal(), а в asyncTearDown() мы еще раз вызовем async_func(). Таким образом async_func() и async_summa() должны отработать программу по 2 раза, async_func() засыпает на пол секунды по два раза, async_summa() на одну по два раза, все складываем и получаем 3 секунды на сон. Теперь запустим программу.
Terminal
$ python -m unittest iso_lated_asyncio_test_case.py Метод assertEqual(x, y) (X == Y) Метод asyncSetUp() Спим 0.5.. Спим 1... Спим 1... Метод asyncTearDown() Спим 0.5.. Тест: iso_lated_asyncio_test_case.Test.test_assert_equal закончен . ---------------------------------------------------------------------- Ran 1 test in 2.512s OK
Отработала программа за 2.5 секунды, потому что при срабатывании asyncSetUp() корутины async_summa() и async_func() вызываются и срабатывают асинхронно, таким образом мы убедились что асинхронность при тестировании отлично работает.
Что дальше?
Мы разобрали все основные возможности unittest, но рекомендую самостоятельно еще раз пробежаться по документации. На unittest тестирование не заканчивается, следующий материал будет посвящен библиотеке pytest.
Тестируйте свой код. Это не сложно и полезно.
Исходный код: Lib/unittest/__init__.py.
(Если вы уже знакомы с основными понятиями тестирования, вы можете пропустить пункт the list of assert methods).
Фреймворк модульного тестирования unittest
изначально был вдохновлен JUnit и имеет схожий вкус с основными фреймворками модульного тестирования в других языках. Он поддерживает автоматизацию тестирования, совместное использование кода настройки и отключения тестов, объединение тестов в коллекции и независимость тестов от фреймворка отчетности.
Для достижения этой цели unittest
поддерживает некоторые важные концепции в объектно-ориентированном виде:
- испытательное приспособление
-
test fixture представляет собой подготовку, необходимую для выполнения одного или нескольких тестов, и любые связанные с этим действия по очистке. Это может включать, например, создание временных или прокси-баз данных, каталогов или запуск серверного процесса.
- тестовый пример
-
test case — это отдельная единица тестирования. Он проверяет наличие конкретной реакции на определенный набор входных данных.
unittest
предоставляет базовый классTestCase
, который можно использовать для создания новых тестовых случаев. - набор тестов
-
test suite — это коллекция тестовых случаев, наборов тестов или и того, и другого. Он используется для объединения тестов, которые должны выполняться вместе.
- испытательный стенд
-
test runner — это компонент, который организует выполнение тестов и предоставляет результат пользователю. Выполнитель может использовать графический интерфейс, текстовый интерфейс или возвращать специальное значение для указания результатов выполнения тестов.
См.также
- Модуль
doctest
-
Еще один модуль поддержки тестирования с совершенно другим вкусом.
- Simple Smalltalk Testing: With Patterns
-
Оригинальная статья Кента Бека о тестировании фреймворков с использованием паттерна, разделяемого
unittest
. - pytest
-
Сторонний unittest framework с облегченным синтаксисом для написания тестов. Например,
assert func(10) == 42
. - The Python Testing Tools Taxonomy
-
Обширный список инструментов тестирования на Python, включая фреймворки для функционального тестирования и библиотеки макетных объектов.
- Testing in Python Mailing List
-
Специальная группа по интересам для обсуждения тестирования и инструментов тестирования на Python.
Сценарий Tools/unittestgui/unittestgui.py
в исходном дистрибутиве Python представляет собой инструмент с графическим интерфейсом для обнаружения и выполнения тестов. Он предназначен в основном для простоты использования новичками в области модульного тестирования. Для производственных сред рекомендуется, чтобы тесты управлялись системой непрерывной интеграции, такой как Buildbot, Jenkins или Travis-CI, или AppVeyor.
Основной пример¶
Модуль unittest
предоставляет богатый набор инструментов для построения и выполнения тестов. Этот раздел демонстрирует, что небольшого подмножества инструментов достаточно для удовлетворения потребностей большинства пользователей.
Вот короткий сценарий для проверки трех строковых методов:
import unittest class TestStringMethods(unittest.TestCase): def test_upper(self): self.assertEqual('foo'.upper(), 'FOO') def test_isupper(self): self.assertTrue('FOO'.isupper()) self.assertFalse('Foo'.isupper()) def test_split(self): s = 'hello world' self.assertEqual(s.split(), ['hello', 'world']) # check that s.split fails when the separator is not a string with self.assertRaises(TypeError): s.split(2) if __name__ == '__main__': unittest.main()
Тест-кейс создается путем подклассификации unittest.TestCase
. Три отдельных теста определяются методами, имена которых начинаются с букв test
. Это соглашение об именовании информирует программу запуска тестов о том, какие методы представляют тесты.
Суть каждого теста заключается в вызове assertEqual()
для проверки ожидаемого результата; assertTrue()
или assertFalse()
для проверки условия; или assertRaises()
для проверки возникновения определенного исключения. Эти методы используются вместо оператора assert
, чтобы программа тестирования могла накапливать все результаты тестирования и создавать отчет.
Методы setUp()
и tearDown()
позволяют определить инструкции, которые будут выполняться до и после каждого метода тестирования. Более подробно они рассматриваются в разделе Организация тестового кода.
В последнем блоке показан простой способ запуска тестов. unittest.main()
предоставляет интерфейс командной строки к тестовому сценарию. При запуске из командной строки вышеприведенный сценарий выдает результат, который выглядит следующим образом:
... ---------------------------------------------------------------------- Ran 3 tests in 0.000s OK
Передача опции -v
в ваш тестовый сценарий даст команду unittest.main()
включить более высокий уровень многословия и выдать следующий результат:
test_isupper (__main__.TestStringMethods) ... ok test_split (__main__.TestStringMethods) ... ok test_upper (__main__.TestStringMethods) ... ok ---------------------------------------------------------------------- Ran 3 tests in 0.001s OK
Приведенные выше примеры показывают наиболее часто используемые возможности unittest
, которых достаточно для удовлетворения многих повседневных потребностей тестирования. В остальной части документации полный набор функций рассматривается с первых принципов.
Интерфейс командной строки¶
Модуль unittest можно использовать из командной строки для запуска тестов из модулей, классов или даже отдельных методов тестирования:
python -m unittest test_module1 test_module2 python -m unittest test_module.TestClass python -m unittest test_module.TestClass.test_method
Вы можете передать список с любой комбинацией имен модулей и полностью определенных имен классов или методов.
Тестовые модули могут быть указаны также по пути к файлу:
python -m unittest tests/test_something.py
Это позволяет вам использовать завершение имени файла оболочки для указания тестового модуля. Указанный файл должен быть импортируемым в качестве модуля. Путь преобразуется в имя модуля путем удаления „.py“ и преобразования разделителей пути в „.“. Если вы хотите выполнить тестовый файл, который не может быть импортирован как модуль, вам следует выполнить файл напрямую.
Вы можете запускать тесты с большей детализацией (более высокой степенью многословности), передавая флаг -v:
python -m unittest -v test_module
При выполнении без аргументов запускается Тестовое обнаружение:
Для получения списка всех опций командной строки:
Изменено в версии 3.2: В предыдущих версиях можно было запускать только отдельные методы тестирования, но не модули или классы.
Параметры командной строки¶
unittest поддерживает эти параметры командной строки:
-
-b
,
--buffer
¶
-
Потоки стандартного вывода и стандартной ошибки буферизируются во время выполнения теста. Вывод при прохождении теста отбрасывается. При провале или ошибке теста вывод повторяется в обычном режиме и добавляется к сообщениям об ошибках.
-
-c
,
--catch
¶
-
Control-C во время выполнения теста ожидает окончания текущего теста и затем сообщает все результаты на данный момент. Второй Control-C вызывает обычное исключение
KeyboardInterrupt
.Функции, обеспечивающие эту функциональность, см. в Signal Handling.
-
-f
,
--failfast
¶
-
Остановите выполнение теста при первой ошибке или сбое.
-
-k
¶
-
Запускать только те тестовые методы и классы, которые соответствуют шаблону или подстроке. Этот параметр можно использовать несколько раз, в этом случае будут включены все тестовые случаи, соответствующие любому из заданных шаблонов.
Шаблоны, содержащие символ подстановки (
*
), сопоставляются с именем теста с помощьюfnmatch.fnmatchcase()
; в противном случае используется простое сопоставление подстрок с учетом регистра.Шаблоны сопоставляются с полным именем метода тестирования, импортированным загрузчиком тестов.
Например,
-k foo
соответствуетfoo_tests.SomeTest.test_something
,bar_tests.SomeTest.test_foo
, но неbar_tests.FooTest.test_something
.
-
--locals
¶
-
Показывать локальные переменные в трассировках.
Добавлено в версии 3.2: Добавлены опции командной строки -b
, -c
и -f
.
Добавлено в версии 3.5: Опция командной строки --locals
.
Добавлено в версии 3.7: Опция командной строки -k
.
Командная строка также может использоваться для обнаружения тестов, для запуска всех тестов в проекте или только их подмножества.
Тестовое обнаружение¶
Добавлено в версии 3.2.
Unittest поддерживает простое обнаружение тестов. Для совместимости с обнаружением тестов все тестовые файлы должны быть modules или packages (включая namespace packages) импортируемыми из каталога верхнего уровня проекта (это означает, что их имена файлов должны быть допустимыми identifiers).
Обнаружение тестов реализовано в TestLoader.discover()
, но также может быть использовано из командной строки. Базовое использование командной строки следующее:
cd project_directory python -m unittest discover
Примечание
В качестве сокращения, python -m unittest
является эквивалентом python -m unittest discover
. Если вы хотите передать аргументы для обнаружения теста, необходимо явно использовать подкоманду discover
.
Подкоманда discover
имеет следующие опции:
-
-v
,
--verbose
¶
-
Подробный вывод
-
-s
,
--start-directory
directory
¶ -
Каталог для начала обнаружения (
.
по умолчанию)
-
-p
,
--pattern
pattern
¶ -
Шаблон для сопоставления тестовых файлов (
test*.py
по умолчанию)
-
-t
,
--top-level-directory
directory
¶ -
Каталог верхнего уровня проекта (по умолчанию — начальный каталог)
Опции -s
, -p
и -t
могут быть переданы в качестве позиционных аргументов в таком порядке. Следующие две командные строки эквивалентны:
python -m unittest discover -s project_directory -p "*_test.py" python -m unittest discover project_directory "*_test.py"
Помимо пути, в качестве начального каталога можно передать имя пакета, например myproject.subpackage.test
. Имя пакета, которое вы передадите, будет импортировано, и его местоположение в файловой системе будет использовано в качестве начального каталога.
Осторожно
Test discovery загружает тесты путем их импорта. После того, как test discovery нашел все файлы тестов из указанного вами начального каталога, он преобразует пути в имена пакетов для импорта. Например, foo/bar/baz.py
будет импортирован как foo.bar.baz
.
Если пакет установлен глобально, а вы пытаетесь проверить test discovery на другой копии пакета, то импорт может произойти не из того места. Если это произойдет, test discovery предупредит вас и завершит работу.
Если вы предоставите начальный каталог как имя пакета, а не как путь к каталогу, то discover будет считать, что то место, из которого он импортирует, является тем местом, которое вы выбрали, поэтому вы не получите предупреждение.
Тестовые модули и пакеты могут настраивать загрузку и обнаружение тестов с помощью load_tests protocol.
Изменено в версии 3.4: Тестовое обнаружение поддерживает namespace packages для начального каталога. Обратите внимание, что необходимо указать и каталог верхнего уровня (например, python -m unittest discover -s root/namespace -t root
).
Организация тестового кода¶
Основными строительными блоками модульного тестирования являются test cases — отдельные сценарии, которые должны быть заданы и проверены на корректность. В unittest
тестовые случаи представлены экземплярами unittest.TestCase
. Для создания собственных тестовых случаев необходимо написать подклассы TestCase
или использовать FunctionTestCase
.
Код тестирования экземпляра TestCase
должен быть полностью самодостаточным, таким, чтобы его можно было запускать либо изолированно, либо в произвольной комбинации с любым количеством других тестовых примеров.
Самый простой подкласс TestCase
будет просто реализовывать метод тестирования (т.е. метод, имя которого начинается с test
) для выполнения определенного кода тестирования:
import unittest class DefaultWidgetSizeTestCase(unittest.TestCase): def test_default_widget_size(self): widget = Widget('The widget') self.assertEqual(widget.size(), (50, 50))
Обратите внимание, что для того, чтобы что-то протестировать, мы используем один из методов assert*()
, предоставляемых базовым классом TestCase
. Если тест завершится неудачно, будет вызвано исключение с поясняющим сообщением, а unittest
идентифицирует тестовый случай как failure. Любые другие исключения будут рассматриваться как errors.
Тестов может быть много, и их настройка может быть повторяющейся. К счастью, мы можем отказаться от кода настройки, реализовав метод setUp()
, который фреймворк тестирования будет автоматически вызывать для каждого запускаемого теста:
import unittest class WidgetTestCase(unittest.TestCase): def setUp(self): self.widget = Widget('The widget') def test_default_widget_size(self): self.assertEqual(self.widget.size(), (50,50), 'incorrect default size') def test_widget_resize(self): self.widget.resize(100,150) self.assertEqual(self.widget.size(), (100,150), 'wrong size after resize')
Примечание
Порядок, в котором будут выполняться различные тесты, определяется путем сортировки имен тестовых методов с учетом встроенного упорядочивания для строк.
Если метод setUp()
вызывает исключение во время выполнения теста, фреймворк будет считать, что в тесте произошла ошибка, и метод теста не будет выполнен.
Аналогично, мы можем предоставить метод tearDown()
, который наводит порядок после выполнения метода тестирования:
import unittest class WidgetTestCase(unittest.TestCase): def setUp(self): self.widget = Widget('The widget') def tearDown(self): self.widget.dispose()
Если setUp()
прошел успешно, tearDown()
будет запущен независимо от того, прошел метод тестирования успешно или нет.
Такая рабочая среда для кода тестирования называется test fixture. Новый экземпляр TestCase создается как уникальное тестовое приспособление, используемое для выполнения каждого отдельного метода тестирования. Таким образом, setUp()
, tearDown()
и __init__()
будут вызываться по одному разу на каждый тест.
Рекомендуется использовать реализацию TestCase для группировки тестов в соответствии с проверяемыми ими функциями. unittest
предоставляет механизм для этого: test suite, представленный классом unittest
TestSuite
. В большинстве случаев вызов unittest.main()
сделает все правильно, соберет для вас все тестовые случаи модуля и выполнит их.
Однако, если вы хотите настроить создание вашего набора тестов, вы можете сделать это самостоятельно:
def suite(): suite = unittest.TestSuite() suite.addTest(WidgetTestCase('test_default_widget_size')) suite.addTest(WidgetTestCase('test_widget_resize')) return suite if __name__ == '__main__': runner = unittest.TextTestRunner() runner.run(suite())
Вы можете поместить определения тестовых случаев и тестовых наборов в те же модули, что и тестируемый код (например, widget.py
), но есть несколько преимуществ размещения тестового кода в отдельном модуле, например, test_widget.py
:
-
Модуль тестирования может быть запущен автономно из командной строки.
-
Тестовый код легче отделить от поставляемого кода.
-
Уменьшается соблазн изменить тестовый код в соответствии с тестируемым кодом без веской причины.
-
Тестовый код должен модифицироваться гораздо реже, чем код, который он тестирует.
-
Протестированный код легче поддается рефакторингу.
-
Тесты для модулей, написанных на C, в любом случае должны быть в отдельных модулях, так почему бы не быть последовательными?
-
Если стратегия тестирования меняется, нет необходимости менять исходный код.
Повторное использование старого тестового кода¶
Некоторые пользователи обнаружат, что у них есть существующий тестовый код, который они хотели бы запускать из unittest
, не преобразуя каждую старую тестовую функцию в подкласс TestCase
.
По этой причине unittest
предоставляет класс FunctionTestCase
. Этот подкласс TestCase
можно использовать для обертывания существующей тестовой функции. Также могут быть предусмотрены функции установки и разрушения.
Дана следующая тестовая функция:
def testSomething(): something = makeSomething() assert something.name is not None # ...
можно создать эквивалентный экземпляр тестового случая следующим образом, с дополнительными методами установки и разрушения:
testcase = unittest.FunctionTestCase(testSomething, setUp=makeSomethingDB, tearDown=deleteSomethingDB)
Примечание
Несмотря на то, что FunctionTestCase
можно использовать для быстрого преобразования существующей базы тестов в систему, основанную на unittest
, такой подход не рекомендуется. Если потратить время на создание правильных подклассов TestCase
, то будущие рефакторинги тестов станут бесконечно проще.
В некоторых случаях существующие тесты могут быть написаны с использованием модуля doctest
. В этом случае doctest
предоставляет класс DocTestSuite
, который может автоматически создавать unittest.TestSuite
экземпляры из существующих doctest
-основанных тестов.
Пропуск тестов и ожидаемые неудачи¶
Добавлено в версии 3.1.
Unittest поддерживает пропуск отдельных методов теста и даже целых классов тестов. Кроме того, он поддерживает пометку теста как «ожидаемого провала», т.е. теста, который сломан и будет провален, но не должен быть засчитан как провал при TestResult
.
Пропуск теста — это просто использование skip()
decorator или одного из его условных вариантов, вызов TestCase.skipTest()
внутри setUp()
или метода теста, или прямое поднятие SkipTest
.
Базовый пропуск выглядит следующим образом:
class MyTestCase(unittest.TestCase): @unittest.skip("demonstrating skipping") def test_nothing(self): self.fail("shouldn't happen") @unittest.skipIf(mylib.__version__ < (1, 3), "not supported in this library version") def test_format(self): # Tests that work for only a certain version of the library. 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
Вот результат выполнения приведенного выше примера в режиме verbose:
test_format (__main__.MyTestCase) ... skipped 'not supported in this library version' test_nothing (__main__.MyTestCase) ... skipped 'demonstrating skipping' test_maybe_skipped (__main__.MyTestCase) ... skipped 'external resource not available' test_windows_support (__main__.MyTestCase) ... skipped 'requires Windows' ---------------------------------------------------------------------- Ran 4 tests in 0.005s OK (skipped=4)
Классы можно пропускать так же, как и методы:
@unittest.skip("showing class skipping") class MySkippedTestCase(unittest.TestCase): def test_not_run(self): pass
TestCase.setUp()
может также пропустить тест. Это полезно, когда ресурс, который необходимо установить, недоступен.
Ожидаемые сбои используют декоратор expectedFailure()
.
class ExpectedFailureTestCase(unittest.TestCase): @unittest.expectedFailure def test_fail(self): self.assertEqual(1, 0, "broken")
Легко создать свои собственные декораторы пропуска теста, создав декоратор, который вызывает skip()
на тесте, когда хочет, чтобы он был пропущен. Этот декоратор пропускает тест, если переданный объект не имеет определенного атрибута:
def skipUnlessHasattr(obj, attr): if hasattr(obj, attr): return lambda func: func return unittest.skip("{!r} doesn't have {!r}".format(obj, attr))
Следующие декораторы и исключения реализуют пропуск тестов и ожидаемые сбои:
-
@
unittest.
skip
(reason)¶ -
Безоговорочно пропустить декорированный тест. Причина должна описывать, почему тест пропускается.
-
@
unittest.
skipIf
(condition, reason)¶ -
Пропустите украшенный тест, если условие истинно.
-
@
unittest.
skipUnless
(condition, reason)¶ -
Пропустите тест decorated, если условие не истинно.
-
@
unittest.
expectedFailure
¶ -
Пометьте тест как ожидаемый провал или ошибку. Если тест терпит неудачу или ошибку в самой тестовой функции (а не в одном из методов test fixture), то он будет считаться успешным. Если тест пройдет, то он будет считаться неудачным.
-
exception
unittest.
SkipTest
(reason)¶ -
Это исключение возникает, чтобы пропустить тест.
Обычно вы можете использовать
TestCase.skipTest()
или один из декораторов пропуска вместо того, чтобы вызывать это напрямую.
У пропущенных тестов не будет пробега setUp()
или tearDown()
вокруг них. В пропущенных классах не будет выполняться setUpClass()
или tearDownClass()
. В пропущенных модулях не будет выполняться setUpModule()
или tearDownModule()
.
Различение итераций теста с помощью субтестов¶
Добавлено в версии 3.4.
Когда между вашими тестами есть очень небольшие различия, например, некоторые параметры, unittest позволяет различать их внутри тела метода теста с помощью менеджера контекста subTest()
.
Например, следующий тест:
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)
приведет к следующему результату:
====================================================================== 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
Без использования подтеста выполнение остановится после первого сбоя, и ошибку будет не так легко диагностировать, поскольку значение i
не будет отображаться:
====================================================================== FAIL: test_even (__main__.NumbersTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "subtests.py", line 32, in test_even self.assertEqual(i % 2, 0) AssertionError: 1 != 0
Классы и функции¶
В этом разделе подробно описывается API unittest
.
Тестовые случаи¶
-
class
unittest.
TestCase
(methodName=‘runTest’)¶ -
Экземпляры класса
TestCase
представляют логические единицы тестирования во вселеннойunittest
. Этот класс предназначен для использования в качестве базового класса, а конкретные тесты реализуются конкретными подклассами. Этот класс реализует интерфейс, необходимый программе запуска тестов, чтобы она могла управлять тестами, и методы, которые тестовый код может использовать для проверки и сообщения о различных видах сбоев.Каждый экземпляр
TestCase
будет выполнять один базовый метод: метод с именем methodName. В большинстве случаев использованияTestCase
вы не будете ни изменять methodName, ни переименовывать метод по умолчаниюrunTest()
.Изменено в версии 3.2:
TestCase
может быть успешно инстанцирован без указания methodName. Это облегчает эксперименты сTestCase
из интерактивного интерпретатора.Экземпляры
TestCase
предоставляют три группы методов: одна группа используется для запуска теста, другая используется реализацией теста для проверки условий и сообщения о сбоях, а также некоторые методы опроса, позволяющие собирать информацию о самом тесте.Методы в первой группе (выполнение теста) следующие:
-
setUp
()¶ -
Метод, вызываемый для подготовки приспособления для тестирования. Он вызывается непосредственно перед вызовом метода тестирования; кроме
AssertionError
илиSkipTest
, любое исключение, вызванное этим методом, будет рассматриваться как ошибка, а не как сбой теста. Реализация по умолчанию ничего не делает.
-
tearDown
()¶ -
Метод вызывается сразу после вызова метода тестирования и записи результата. Этот метод вызывается, даже если метод теста вызвал исключение, поэтому реализация в подклассах может потребовать особой осторожности при проверке внутреннего состояния. Любое исключение, отличное от
AssertionError
илиSkipTest
, вызванное этим методом, будет рассматриваться как дополнительная ошибка, а не как сбой теста (таким образом, увеличивая общее количество зарегистрированных ошибок). Этот метод будет вызван только в случае успехаsetUp()
, независимо от результата метода тестирования. Реализация по умолчанию ничего не делает.
-
setUpClass
()¶ -
Метод класса, вызываемый перед выполнением тестов в отдельном классе.
setUpClass
вызывается с классом в качестве единственного аргумента и должен быть оформлен какclassmethod()
:@classmethod def setUpClass(cls): ...
Более подробную информацию см. в разделе Class and Module Fixtures.
Добавлено в версии 3.2.
-
tearDownClass
()¶ -
Метод класса, вызываемый после выполнения тестов в отдельном классе.
tearDownClass
вызывается с классом в качестве единственного аргумента и должен быть оформлен какclassmethod()
:@classmethod def tearDownClass(cls): ...
Более подробную информацию см. в разделе Class and Module Fixtures.
Добавлено в версии 3.2.
-
run
(result=None)¶ -
Выполните тест, собирая результат в объект
TestResult
, переданный в качестве result. Если result опущен илиNone
, создается временный объект result (путем вызова методаdefaultTestResult()
) и используется. Объект результата возвращается вызывающей сторонеrun()
.Того же эффекта можно добиться, просто вызвав экземпляр
TestCase
.Изменено в версии 3.3: Предыдущие версии
run
не возвращали результат. Также как и вызов экземпляра.
-
skipTest
(reason)¶ -
Вызов этой функции во время метода тестирования или
setUp()
пропускает текущий тест. Для получения дополнительной информации см. раздел Пропуск тестов и ожидаемые неудачи.Добавлено в версии 3.1.
-
subTest
(msg=None, **params)¶ -
Возвращает контекстный менеджер, который выполняет вложенный блок кода в качестве подтеста. msg и params — это необязательные, произвольные значения, которые отображаются при каждом сбое подтеста, позволяя вам четко их идентифицировать.
Тестовый пример может содержать любое количество объявлений подтестов, и они могут быть произвольно вложены друг в друга.
Для получения дополнительной информации см. раздел Различение итераций теста с помощью субтестов.
Добавлено в версии 3.4.
-
debug
()¶ -
Выполнить тест без сбора результатов. Это позволяет исключениям, вызванным тестом, передаваться вызывающей стороне, и может использоваться для поддержки запуска тестов под отладчиком.
Класс
TestCase
предоставляет несколько методов assert для проверки и сообщения о сбоях. В следующей таблице перечислены наиболее часто используемые методы (дополнительные методы assert см. в таблицах ниже):Метод
Проверяет, что
Новое в
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
3.1
assertIsNot(a, b)
a is not b
3.1
assertIsNone(x)
x is None
3.1
assertIsNotNone(x)
x is not None
3.1
assertIn(a, b)
a in b
3.1
assertNotIn(a, b)
a not in b
3.1
assertIsInstance(a, b)
isinstance(a, b)
3.2
assertNotIsInstance(a, b)
not isinstance(a, b)
3.2
Все методы assert принимают аргумент msg, который, если указан, используется в качестве сообщения об ошибке при неудаче (см. также
longMessage
). Обратите внимание, что аргумент msg может быть передан вassertRaises()
,assertRaisesRegex()
,assertWarns()
,assertWarnsRegex()
только при использовании их в качестве менеджера контекста.-
assertEqual
(first, second, msg=None)¶ -
Проверьте, что первое и второе равны. Если значения не равны, тест будет провален.
Кроме того, если first и second являются одинаковыми типами и одним из list, tuple, dict, set, frozenset или str или любым типом, который подкласс регистрирует с помощью
addTypeEqualityFunc()
, будет вызвана специфическая для типа функция равенства, чтобы сгенерировать более полезное сообщение об ошибке по умолчанию (см. также list of type-specific methods).Изменено в версии 3.1: Добавлен автоматический вызов специфической для типа функции равенства.
Изменено в версии 3.2:
assertMultiLineEqual()
добавлена в качестве функции равенства типов по умолчанию для сравнения строк.
-
assertNotEqual
(first, second, msg=None)¶ -
Проверьте, что первое и второе не равны. Если значения равны, тест завершится неудачей.
-
assertTrue
(expr, msg=None)¶ -
assertFalse
(expr, msg=None)¶ -
Проверьте, что expr является истинным (или ложным).
Обратите внимание, что это эквивалентно
bool(expr) is True
, а неexpr is True
(для последнего используйтеassertIs(expr, True)
). Этого метода также следует избегать, если доступны более специфические методы (например,assertEqual(a, b)
вместоassertTrue(a == b)
), поскольку они обеспечивают лучшее сообщение об ошибке в случае неудачи.
-
assertIs
(first, second, msg=None)¶ -
assertIsNot
(first, second, msg=None)¶ -
Проверьте, что first и second являются (или не являются) одним и тем же объектом.
Добавлено в версии 3.1.
-
assertIsNone
(expr, msg=None)¶ -
assertIsNotNone
(expr, msg=None)¶ -
Проверьте, что expr является (или не является)
None
.Добавлено в версии 3.1.
-
assertIn
(member, container, msg=None)¶ -
assertNotIn
(member, container, msg=None)¶ -
Проверьте, что член находится (или не находится) в контейнере.
Добавлено в версии 3.1.
-
assertIsInstance
(obj, cls, msg=None)¶ -
assertNotIsInstance
(obj, cls, msg=None)¶ -
Проверьте, что obj является (или не является) экземпляром cls (который может быть классом или кортежем классов, как поддерживается
isinstance()
). Чтобы проверить точный тип, используйтеassertIs(type(obj), cls)
.Добавлено в версии 3.2.
Также можно проверить производство исключений, предупреждений и сообщений журнала, используя следующие методы:
Метод
Проверяет, что
Новое в
assertRaises(exc, fun, *args, **kwds)
fun(*args, **kwds)
вызывает exc.assertRaisesRegex(exc, r, fun, *args, **kwds)
fun(*args, **kwds)
вызывает exc и сообщение соответствует regex r3.1
assertWarns(warn, fun, *args, **kwds)
fun(*args, **kwds)
вызывает warn.3.2
assertWarnsRegex(warn, r, fun, *args, **kwds)
fun(*args, **kwds)
вызывает предупреждение warn и сообщение соответствует regex r3.2
assertLogs(logger, level)
Блок
with
регистрирует журнал на logger с минимальным уровнем.3.4
assertNoLogs(logger, level)
- Блок
with
не входит в систему -
логгер с минимальным уровнем.
3.10
-
assertRaises
(exception, callable, *args, **kwds)¶ -
assertRaises
(exception, *, msg=None) -
Проверяет, что при вызове callable с любыми позиционными или ключевыми аргументами, которые также передаются в
assertRaises()
, возникает исключение. Тест проходит, если вызвано exception, является ошибкой, если вызвано другое исключение, или проваливается, если исключение не вызвано. Чтобы перехватить любое из группы исключений, в качестве exception можно передать кортеж, содержащий классы исключений.Если заданы только аргументы exception и, возможно, msg, верните менеджер контекста, чтобы тестируемый код можно было написать inline, а не как функцию:
with self.assertRaises(SomeException): do_something()
При использовании в качестве менеджера контекста
assertRaises()
принимает дополнительный аргумент в виде ключевого слова msg.Менеджер контекста будет хранить объект пойманного исключения в своем атрибуте
exception
. Это может быть полезно, если предполагается выполнить дополнительные проверки поднятого исключения:with self.assertRaises(SomeException) as cm: do_something() the_exception = cm.exception self.assertEqual(the_exception.error_code, 3)
Изменено в версии 3.1: Добавлена возможность использовать
assertRaises()
в качестве менеджера контекста.Изменено в версии 3.2: Добавлен атрибут
exception
.Изменено в версии 3.3: Добавлен аргумент ключевого слова msg при использовании в качестве менеджера контекста.
-
assertRaisesRegex
(exception, regex, callable, *args, **kwds)¶ -
assertRaisesRegex
(exception, regex, *, msg=None) -
Как
assertRaises()
, но проверяет, что regex совпадает со строковым представлением поднятого исключения. regex может быть объектом регулярного выражения или строкой, содержащей регулярное выражение, подходящее для использованияre.search()
. Примеры:self.assertRaisesRegex(ValueError, "invalid literal for.*XYZ'$", int, 'XYZ')
или:
with self.assertRaisesRegex(ValueError, 'literal'): int('XYZ')
Добавлено в версии 3.1: Добавлено под именем
assertRaisesRegexp
.Изменено в версии 3.2: Переименовано в
assertRaisesRegex()
.Изменено в версии 3.3: Добавлен аргумент ключевого слова msg при использовании в качестве менеджера контекста.
-
assertWarns
(warning, callable, *args, **kwds)¶ -
assertWarns
(warning, *, msg=None) -
Проверяет, что при вызове callable с любыми позиционными или ключевыми аргументами, которые также передаются в
assertWarns()
, срабатывает предупреждение. Тест проходит, если срабатывает warning, и не проходит, если не срабатывает. Любое исключение является ошибкой. Чтобы перехватить любое из группы предупреждений, в качестве warnings можно передать кортеж, содержащий классы предупреждений.Если заданы только аргументы warning и, возможно, msg, верните менеджер контекста, чтобы тестируемый код можно было написать inline, а не как функцию:
with self.assertWarns(SomeWarning): do_something()
При использовании в качестве менеджера контекста
assertWarns()
принимает дополнительный аргумент в виде ключевого слова msg.Менеджер контекста будет хранить объект пойманного предупреждения в его атрибуте
warning
, а исходную строку, вызвавшую предупреждение, в атрибутахfilename
иlineno
. Это может быть полезно, если предполагается выполнить дополнительные проверки пойманного предупреждения:with self.assertWarns(SomeWarning) as cm: do_something() self.assertIn('myfile.py', cm.filename) self.assertEqual(320, cm.lineno)
Этот метод работает независимо от фильтров предупреждения, установленных на момент его вызова.
Добавлено в версии 3.2.
Изменено в версии 3.3: Добавлен аргумент ключевого слова msg при использовании в качестве менеджера контекста.
-
assertWarnsRegex
(warning, regex, callable, *args, **kwds)¶ -
assertWarnsRegex
(warning, regex, *, msg=None) -
Аналогично
assertWarns()
, но также проверяет соответствие regex сообщению сработавшего предупреждения. regex может быть объектом регулярного выражения или строкой, содержащей регулярное выражение, подходящее для использованияre.search()
. Пример:self.assertWarnsRegex(DeprecationWarning, r'legacy_function() is deprecated', legacy_function, 'XYZ')
или:
with self.assertWarnsRegex(RuntimeWarning, 'unsafe frobnicating'): frobnicate('/etc/passwd')
Добавлено в версии 3.2.
Изменено в версии 3.3: Добавлен аргумент ключевого слова msg при использовании в качестве менеджера контекста.
-
assertLogs
(logger=None, level=None)¶ -
Менеджер контекста для проверки того, что в logger или в одном из его дочерних элементов зарегистрировано хотя бы одно сообщение с заданным уровнем.
Если задано, logger должен быть объектом
logging.Logger
илиstr
, задающим имя логгера. По умолчанию используется корневой логгер, который будет перехватывать все сообщения, которые не были заблокированы нераспространяющимся нисходящим логгером.Если задано, level должен быть либо числовым уровнем протоколирования, либо его строковым эквивалентом (например,
"ERROR"
илиlogging.ERROR
). По умолчанию используется значениеlogging.INFO
.Тест проходит, если хотя бы одно сообщение, выданное внутри блока
with
, соответствует условиям logger и level, иначе он проваливается.Объект, возвращаемый менеджером контекста, является помощником записи, который отслеживает совпадающие сообщения журнала. Он имеет два атрибута:
-
records
¶ -
Список
logging.LogRecord
объектов соответствующих сообщений журнала.
-
output
¶ -
Список объектов
str
с форматированным выводом совпадающих сообщений.
Пример:
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'])
Добавлено в версии 3.4.
-
-
assertNoLogs
(logger=None, level=None)¶ -
Менеджер контекста для проверки того, что на logger или одном из его дочерних элементов не зарегистрировано никаких сообщений, по крайней мере, с заданным уровнем.
Если задано, logger должен быть объектом
logging.Logger
илиstr
, задающим имя регистратора. По умолчанию используется корневой логгер, который будет перехватывать все сообщения.Если задано, level должен быть либо числовым уровнем протоколирования, либо его строковым эквивалентом (например,
"ERROR"
илиlogging.ERROR
). По умолчанию используется значениеlogging.INFO
.В отличие от
assertLogs()
, менеджер контекста ничего не возвращает.Добавлено в версии 3.10.
Существуют и другие методы, используемые для проведения более специфических проверок, например:
Метод
Проверяет, что
Новое в
assertAlmostEqual(a, b)
round(a-b, 7) == 0
assertNotAlmostEqual(a, b)
round(a-b, 7) != 0
assertGreater(a, b)
a > b
3.1
assertGreaterEqual(a, b)
a >= b
3.1
assertLess(a, b)
a < b
3.1
assertLessEqual(a, b)
a <= b
3.1
assertRegex(s, r)
r.search(s)
3.1
assertNotRegex(s, r)
not r.search(s)
3.2
assertCountEqual(a, b)
a и b имеют одинаковые элементы в одинаковом количестве, независимо от их порядка.
3.2
-
assertAlmostEqual
(first, second, places=7, msg=None, delta=None)¶ -
assertNotAlmostEqual
(first, second, places=7, msg=None, delta=None)¶ -
Проверьте, что первое и второе приблизительно (или не приблизительно) равны, вычислив разность, округлив до заданного числа десятичных разрядов (по умолчанию 7) и сравнив с нулем. Обратите внимание, что эти методы округляют значения до заданного числа десятичных знаков (т.е. как функция
round()
), а не до значительных цифр.Если вместо мест указана дельта, то разница между первым и вторым должна быть меньше или равна (или больше) дельты.
Если предоставить и delta, и places, то возникнет ошибка
TypeError
.Изменено в версии 3.2:
assertAlmostEqual()
автоматически считает почти равными объекты, которые сравнивают равными.assertNotAlmostEqual()
автоматически терпит неудачу, если объекты сравниваются равными. Добавлен аргумент с ключевым словом delta.
-
assertGreater
(first, second, msg=None)¶ -
assertGreaterEqual
(first, second, msg=None)¶ -
assertLess
(first, second, msg=None)¶ -
assertLessEqual
(first, second, msg=None)¶ -
Проверьте, что first соответственно >, >=, < или <=, чем second в зависимости от имени метода. Если это не так, тест будет провален:
>>> self.assertGreaterEqual(3, 4) AssertionError: "3" unexpectedly not greater than or equal to "4"
Добавлено в версии 3.1.
-
assertRegex
(text, regex, msg=None)¶ -
assertNotRegex
(text, regex, msg=None)¶ -
Проверить, что поиск regex соответствует (или не соответствует) тексту. В случае неудачи сообщение об ошибке будет включать шаблон и текст (или шаблон и часть текста, которая неожиданно совпала). regex может быть объектом регулярного выражения или строкой, содержащей регулярное выражение, подходящее для использования
re.search()
.Добавлено в версии 3.1: Добавлено под именем
assertRegexpMatches
.Изменено в версии 3.2: Метод
assertRegexpMatches()
был переименован вassertRegex()
.Добавлено в версии 3.2:
assertNotRegex()
.Добавлено в версии 3.5: Имя
assertNotRegexpMatches
является устаревшим псевдонимом дляassertNotRegex()
.
-
assertCountEqual
(first, second, msg=None)¶ -
Проверьте, что последовательность первая содержит те же элементы, что и вторая, независимо от их порядка. Если это не так, будет выдано сообщение об ошибке с перечислением различий между последовательностями.
Дублирующиеся элементы не игнорируются при сравнении первого и второго. Проверяется, имеет ли каждый элемент одинаковое количество в обеих последовательностях. Эквивалентно:
assertEqual(Counter(list(first)), Counter(list(second)))
, но работает и с последовательностями нехешируемых объектов.Добавлено в версии 3.2.
Метод
assertEqual()
передает проверку равенства для объектов одного типа различным методам, специфичным для конкретного типа. Эти методы уже реализованы для большинства встроенных типов, но также можно зарегистрировать новые методы с помощьюaddTypeEqualityFunc()
:-
addTypeEqualityFunc
(typeobj, function)¶ -
Регистрирует специфический для типа метод, вызываемый
assertEqual()
для проверки равенства двух объектов одного и того же typeobj (не подклассов). function должна принимать два позиционных аргумента и третий аргумент с ключевым словом msg=None, как иassertEqual()
. Она должна вызывать сигналself.failureException(msg)
при обнаружении неравенства между первыми двумя параметрами — возможно, предоставляя полезную информацию и подробно объясняя неравенство в сообщении об ошибке.Добавлено в версии 3.1.
Список специфических для типов методов, автоматически используемых
assertEqual()
, приведен в следующей таблице. Обратите внимание, что обычно нет необходимости вызывать эти методы напрямую.Метод
Используется для сравнения
Новое в
assertMultiLineEqual(a, b)
строки
3.1
assertSequenceEqual(a, b)
последовательности
3.1
assertListEqual(a, b)
списки
3.1
assertTupleEqual(a, b)
кортежи
3.1
assertSetEqual(a, b)
наборы или заморозки
3.1
assertDictEqual(a, b)
dicts
3.1
-
assertMultiLineEqual
(first, second, msg=None)¶ -
Проверьте, равна ли многострочная строка первая строке вторая. Если не равна, в сообщение об ошибке будет включена разница между двумя строками, подчеркивающая различия. Этот метод используется по умолчанию при сравнении строк с помощью
assertEqual()
.Добавлено в версии 3.1.
-
assertSequenceEqual
(first, second, msg=None, seq_type=None)¶ -
Проверяет равенство двух последовательностей. Если задан seq_type, то и first и second должны быть экземплярами seq_type, иначе будет выдан сбой. Если последовательности разные, выдается сообщение об ошибке, в котором указывается разница между ними.
Этот метод не вызывается непосредственно
assertEqual()
, но он используется для реализацииassertListEqual()
иassertTupleEqual()
.Добавлено в версии 3.1.
-
assertListEqual
(first, second, msg=None)¶ -
assertTupleEqual
(first, second, msg=None)¶ -
Проверяет, равны ли два списка или кортежа. Если нет, то выдается сообщение об ошибке, в котором указываются только различия между ними. Ошибка также выдается, если один из параметров имеет неправильный тип. Эти методы используются по умолчанию при сравнении списков или кортежей с помощью
assertEqual()
.Добавлено в версии 3.1.
-
assertSetEqual
(first, second, msg=None)¶ -
Проверяет, что два набора равны. Если нет, то строится сообщение об ошибке, в котором перечисляются различия между множествами. Этот метод используется по умолчанию при сравнении множеств или замороженных множеств с помощью
assertEqual()
.Неудача, если у first или second нет метода
set.difference()
.Добавлено в версии 3.1.
-
assertDictEqual
(first, second, msg=None)¶ -
Проверьте, что два словаря равны. Если нет, то строится сообщение об ошибке, показывающее различия в словарях. Этот метод будет использоваться по умолчанию для сравнения словарей в вызовах
assertEqual()
.Добавлено в версии 3.1.
Наконец,
TestCase
предоставляет следующие методы и атрибуты:-
fail
(msg=None)¶ -
Сигнализирует о неудаче теста безусловно, с msg или
None
для сообщения об ошибке.
-
failureException
¶ -
Этот атрибут класса задает исключение, вызванное методом тестирования. Если тестовому фреймворку необходимо использовать специализированное исключение, возможно, несущее дополнительную информацию, он должен подклассифицировать это исключение, чтобы «играть честно» с фреймворком. Начальное значение этого атрибута —
AssertionError
.
-
longMessage
¶ -
Этот атрибут класса определяет, что произойдет, если пользовательское сообщение о сбое будет передано в качестве аргумента msg вызову assertXYY, который завершился неудачей. По умолчанию используется значение
True
. В этом случае пользовательское сообщение добавляется в конец стандартного сообщения о сбое. Если установлено значениеFalse
, пользовательское сообщение заменяет стандартное.Настройку класса можно переопределить в отдельных методах тестирования, присвоив атрибуту экземпляра self.longMessage значение
True
илиFalse
перед вызовом методов assert.Настройка класса сбрасывается перед каждым тестовым вызовом.
Добавлено в версии 3.1.
-
maxDiff
¶ -
Этот атрибут контролирует максимальную длину диффов, выводимых методами assert, которые сообщают о диффах при неудаче. По умолчанию он равен 80*8 символов. Этот атрибут влияет на методы assert:
assertSequenceEqual()
(включая все методы сравнения последовательностей, которые делегируются ему),assertDictEqual()
иassertMultiLineEqual()
.Установка
maxDiff
вNone
означает, что максимальной длины диффов не существует.Добавлено в версии 3.2.
Механизмы тестирования могут использовать следующие методы для сбора информации о тесте:
-
countTestCases
()¶ -
Возвращает количество тестов, представленных данным объектом теста. Для экземпляров
TestCase
это всегда будет1
.
-
defaultTestResult
()¶ -
Возвращает экземпляр класса результатов тестирования, который должен использоваться для данного класса тестового случая (если методу
run()
не предоставлен другой экземпляр результата).Для экземпляров
TestCase
это всегда будет экземплярTestResult
; подклассыTestCase
должны переопределять его по мере необходимости.
-
id
()¶ -
Возвращает строку, идентифицирующую конкретный тестовый случай. Обычно это полное имя метода тестирования, включая имя модуля и класса.
-
shortDescription
()¶ -
Возвращает описание теста, или
None
, если описание не было предоставлено. Реализация этого метода по умолчанию возвращает первую строку документальной строки метода теста, если она доступна, илиNone
.Изменено в версии 3.1: В версии 3.1 это было изменено, чтобы добавлять имя теста в краткое описание даже при наличии doc-строки. Это вызвало проблемы совместимости с расширениями unittest, и в Python 3.2 добавление имени теста было перенесено в
TextTestResult
.
-
addCleanup
(function, /, *args, **kwargs)¶ -
Добавьте функцию, которая будет вызываться после
tearDown()
для очистки ресурсов, использованных во время теста. Функции будут вызываться в порядке, обратном порядку их добавления (LIFO). Они вызываются с любыми аргументами и аргументами ключевых слов, переданными вaddCleanup()
при добавлении.Если
setUp()
потерпит неудачу, то естьtearDown()
не будет вызван, то все добавленные функции очистки все равно будут вызваны.Добавлено в версии 3.1.
-
doCleanups
()¶ -
Этот метод вызывается безусловно после
tearDown()
, или послеsetUp()
, еслиsetUp()
вызывает исключение.Он отвечает за вызов всех функций очистки, добавленных
addCleanup()
. Если вам нужно, чтобы функции очистки вызывались приоритетно кtearDown()
, то вы можете вызватьdoCleanups()
самостоятельно.doCleanups()
выгружает методы из стека функций очистки по одному за раз, поэтому его можно вызвать в любое время.Добавлено в версии 3.1.
-
classmethod
addClassCleanup
(function, /, *args, **kwargs)¶ -
Добавьте функцию, которая будет вызываться после
tearDownClass()
для очистки ресурсов, использованных во время работы тестового класса. Функции будут вызываться в порядке, обратном порядку их добавления (LIFO). Они вызываются с любыми аргументами и аргументами ключевых слов, переданными вaddClassCleanup()
при их добавлении.Если
setUpClass()
потерпит неудачу, то естьtearDownClass()
не будет вызван, то все добавленные функции очистки все равно будут вызваны.Добавлено в версии 3.8.
-
classmethod
doClassCleanups
()¶ -
Этот метод вызывается безусловно после
tearDownClass()
, или послеsetUpClass()
, еслиsetUpClass()
вызывает исключение.Он отвечает за вызов всех функций очистки, добавленных
addClassCleanup()
. Если вам нужно, чтобы функции очистки вызывались приоритетно кtearDownClass()
, то вы можете вызватьdoClassCleanups()
самостоятельно.doClassCleanups()
выгружает методы из стека функций очистки по одному за раз, поэтому его можно вызвать в любое время.Добавлено в версии 3.8.
-
-
class
unittest.
IsolatedAsyncioTestCase
(methodName=‘runTest’)¶ -
Этот класс предоставляет API, аналогичный
TestCase
, и также принимает coroutines в качестве тестовых функций.Добавлено в версии 3.8.
-
coroutine
asyncSetUp
()¶ -
Метод, вызываемый для подготовки приспособления для тестирования. Вызывается после
setUp()
. Вызывается непосредственно перед вызовом метода тестирования; кромеAssertionError
илиSkipTest
, любое исключение, вызванное этим методом, будет рассматриваться как ошибка, а не как сбой теста. Реализация по умолчанию ничего не делает.
-
coroutine
asyncTearDown
()¶ -
Метод, вызываемый сразу после вызова метода тестирования и записи результата. Вызывается перед
tearDown()
. Этот метод вызывается, даже если метод проверки вызвал исключение, поэтому реализация в подклассах может потребовать особой осторожности при проверке внутреннего состояния. Любое исключение, кромеAssertionError
илиSkipTest
, вызванное этим методом, будет рассматриваться как дополнительная ошибка, а не как сбой теста (таким образом, увеличивая общее количество сообщаемых ошибок). Этот метод будет вызван только в случае успехаasyncSetUp()
, независимо от результата метода тестирования. Реализация по умолчанию ничего не делает.
-
addAsyncCleanup
(function, /, *args, **kwargs)¶ -
Этот метод принимает корутину, которая может быть использована в качестве функции очистки.
-
run
(result=None)¶ -
Устанавливает новый цикл событий для выполнения теста, собирая результат в объект
TestResult
, переданный в качестве result. Если result опущен илиNone
, создается временный объект result (путем вызова методаdefaultTestResult()
) и используется. Объект результата возвращается вызывающемуrun()
. По окончании теста все задачи в цикле событий отменяются.
Пример, иллюстрирующий порядок:
from unittest import IsolatedAsyncioTestCase events = [] class Test(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://example.com") 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()
После выполнения теста
events
будет содержать["setUp", "asyncSetUp", "test_response", "asyncTearDown", "tearDown", "cleanup"]
. -
coroutine
-
class
unittest.
FunctionTestCase
(testFunc, setUp=None, tearDown=None, description=None)¶ -
Этот класс реализует часть интерфейса
TestCase
, которая позволяет программе запуска тестов управлять тестом, но не предоставляет методов, которые тестовый код может использовать для проверки и сообщения об ошибках. Он используется для создания тестовых примеров с использованием унаследованного тестового кода, что позволяет интегрировать его в тестовую структуру на основеunittest
.
Утратившие актуальность псевдонимы¶
По историческим причинам некоторые из методов TestCase
имели один или несколько псевдонимов, которые теперь устарели. В следующей таблице перечислены правильные имена вместе с их устаревшими псевдонимами:
Название метода
Утративший силу псевдоним
Утративший силу псевдоним
assertEqual()
failUnlessEqual
assertEquals
assertNotEqual()
failIfEqual
assertNotEquals
assertTrue()
failUnless
утверждение_
assertFalse()
failIf
assertRaises()
failUnlessRaises
assertAlmostEqual()
failUnlessAlmostEqual
assertAlmostEquals
assertNotAlmostEqual()
failIfAlmostEqual
assertNotAlmostEquals
assertRegex()
assertRegexpMatches
assertNotRegex()
assertNotRegexpMatches
assertRaisesRegex()
assertRaisesRegexp
Не рекомендуется, начиная с версии 3.1: Псевдонимы fail*, перечисленные во втором столбце, были устаревшими.
Не рекомендуется, начиная с версии 3.2: Псевдонимы assert*, перечисленные в третьем столбце, были устаревшими.
Не рекомендуется, начиная с версии 3.2:
assertRegexpMatches
иassertRaisesRegexp
были переименованы вassertRegex()
иassertRaisesRegex()
.Не рекомендуется, начиная с версии 3.5: Имя
assertNotRegexpMatches
устарело в пользуassertNotRegex()
.
Группировка тестов¶
-
class
unittest.
TestSuite
(tests=())¶ -
Этот класс представляет собой совокупность отдельных тестовых случаев и тестовых наборов. Класс представляет интерфейс, необходимый программе запуска тестов, чтобы его можно было запускать как любой другой тестовый случай. Запуск экземпляра
TestSuite
аналогичен итерации по набору, выполняя каждый тест по отдельности.Если указано tests, то это должен быть итерабельный список отдельных тестовых случаев или других тестовых наборов, которые будут использоваться для создания набора изначально. Для последующего добавления тестовых случаев и наборов в коллекцию предусмотрены дополнительные методы.
Объекты
TestSuite
ведут себя так же, как объектыTestCase
, за исключением того, что они фактически не реализуют тест. Вместо этого они используются для объединения тестов в группы тестов, которые должны выполняться вместе. Для добавления тестов к экземплярамTestSuite
доступны некоторые дополнительные методы:-
addTest
(test)¶ -
Добавьте
TestCase
илиTestSuite
в набор.
-
addTests
(tests)¶ -
Добавьте все тесты из итерабельного набора экземпляров
TestCase
иTestSuite
в этот набор тестов.Это эквивалентно итерации по тестам, вызывая
addTest()
для каждого элемента.
TestSuite
разделяет следующие методы сTestCase
:-
run
(result)¶ -
Выполните тесты, связанные с этим набором, собирая результат в объект результата теста, переданный как result. Обратите внимание, что в отличие от
TestCase.run()
,TestSuite.run()
требует передачи объекта результата.
-
debug
()¶ -
Запуск тестов, связанных с этим набором, без сбора результатов. Это позволяет исключениям, вызванным тестом, быть переданными вызывающей стороне и может быть использовано для поддержки запуска тестов под отладчиком.
-
countTestCases
()¶ -
Возвращает количество тестов, представленных данным объектом тестирования, включая все отдельные тесты и подтесты.
-
__iter__
()¶ -
Доступ к тестам, сгруппированным по
TestSuite
, всегда осуществляется путем итерации. Подклассы могут лениво предоставлять тесты, переопределяя__iter__()
. Обратите внимание, что этот метод может быть вызван несколько раз для одного набора (например, при подсчете тестов или сравнении на равенство), поэтому тесты, возвращаемые при повторных итерациях доTestSuite.run()
, должны быть одинаковыми для каждой итерации вызова. ПослеTestSuite.run()
вызывающие стороны не должны полагаться на тесты, возвращаемые этим методом, если только вызывающая сторона не использует подкласс, который переопределяетTestSuite._removeTestAtIndex()
для сохранения ссылок на тесты.Изменено в версии 3.2: В ранних версиях
TestSuite
обращались к тестам напрямую, а не через итерацию, поэтому переопределения__iter__()
было недостаточно для предоставления тестов.Изменено в версии 3.4: В ранних версиях
TestSuite
хранил ссылки на каждыйTestCase
послеTestSuite.run()
. Подклассы могут восстановить это поведение, переопределивTestSuite._removeTestAtIndex()
.
При типичном использовании объекта
TestSuite
методrun()
вызываетсяTestRunner
, а не тестовым жгутом конечного пользователя. -
Загрузка и выполнение тестов¶
-
class
unittest.
TestLoader
¶ -
Класс
TestLoader
используется для создания тестовых наборов из классов и модулей. Обычно нет необходимости создавать экземпляр этого класса; модульunittest
предоставляет экземпляр, который можно совместно использовать какunittest.defaultTestLoader
. Однако использование подкласса или экземпляра позволяет настраивать некоторые конфигурируемые свойства.Объекты
TestLoader
имеют следующие атрибуты:-
errors
¶ -
Список нефатальных ошибок, возникших при загрузке тестов. Не сбрасывается загрузчиком в любой момент. Фатальные ошибки сигнализируются соответствующим методом a, вызывающим исключение. Нефатальные ошибки также обозначаются синтетическим тестом, который при запуске выдает исходную ошибку.
Добавлено в версии 3.5.
Объекты
TestLoader
имеют следующие методы:-
loadTestsFromTestCase
(testCaseClass)¶ -
Возвращает набор всех тестовых случаев, содержащихся в
TestCase
— производномtestCaseClass
.Для каждого метода, названного
getTestCaseNames()
, создается экземпляр тестового случая. По умолчанию это имена методов, начинающиеся сtest
. ЕслиgetTestCaseNames()
не возвращает никаких методов, но реализован методrunTest()
, то вместо него создается один тестовый пример для этого метода.
-
loadTestsFromModule
(module, pattern=None)¶ -
Возвращает набор всех тестовых случаев, содержащихся в данном модуле. Этот метод ищет в module классы, производные от
TestCase
и создает экземпляр класса для каждого тестового метода, определенного для класса.Примечание
Хотя использование иерархии
TestCase
-производных классов может быть удобным при совместном использовании приспособлений и вспомогательных функций, определение методов тестирования на базовых классах, которые не предназначены для непосредственного инстанцирования, не очень хорошо сочетается с этим методом. Тем не менее, это может быть полезно, когда приспособления различны и определены в подклассах.Если модуль предоставляет функцию
load_tests
, то она будет вызвана для загрузки тестов. Это позволяет модулям настраивать загрузку тестов. Это и есть load_tests protocol. Аргумент pattern передается в качестве третьего аргумента вload_tests
.Изменено в версии 3.2: Добавлена поддержка
load_tests
.Изменено в версии 3.5: Недокументированный и неофициальный аргумент по умолчанию use_load_tests является устаревшим и игнорируется, хотя он все еще принимается для обратной совместимости. Метод также теперь принимает аргумент pattern, который передается в
load_tests
в качестве третьего аргумента.
-
loadTestsFromName
(name, module=None)¶ -
Возвращает набор всех тестовых случаев, заданных строковым спецификатором.
Спецификатор name — это «точечное имя», которое может разрешаться либо в модуль, либо в класс тестовых примеров, либо в метод тестирования в классе тестовых примеров, либо в экземпляр
TestSuite
, либо в вызываемый объект, который возвращает экземплярTestCase
илиTestSuite
. Эти проверки применяются в порядке, указанном здесь; то есть метод в возможном классе тестового примера будет воспринят как «метод теста в классе тестового примера», а не как «вызываемый объект».Например, если у вас есть модуль
SampleTests
, содержащийTestCase
-производный классSampleTestCase
с тремя тестовыми методами (test_one()
,test_two()
иtest_three()
), спецификатор'SampleTests.SampleTestCase'
заставит этот метод вернуть набор тестов, в котором будут запущены все три тестовых метода. Использование спецификатора'SampleTests.SampleTestCase.test_two'
приведет к тому, что метод вернет набор тестов, в котором будет запущен только методtest_two()
. Спецификатор может ссылаться на модули и пакеты, которые не были импортированы; они будут импортированы как побочный эффект.Метод опционально разрешает name относительно заданного module.
Изменено в версии 3.5: Если при обходе name возникает ошибка
ImportError
илиAttributeError
, то возвращается синтетический тест, который при выполнении вызывает эту ошибку. Эти ошибки включаются в число ошибок, накапливаемых self.errors.
-
loadTestsFromNames
(names, module=None)¶ -
Аналогична
loadTestsFromName()
, но принимает последовательность имен, а не одно имя. Возвращаемое значение — набор тестов, который поддерживает все тесты, определенные для каждого имени.
-
getTestCaseNames
(testCaseClass)¶ -
Возвращает отсортированную последовательность имен методов, найденных в testCaseClass; это должен быть подкласс
TestCase
.
-
discover
(start_dir, pattern=‘test*.py’, top_level_dir=None)¶ -
Находит все тестовые модули, перебирая подкаталоги из указанного начального каталога, и возвращает объект TestSuite, содержащий их. Будут загружены только те тестовые файлы, которые соответствуют шаблону. (Используется сопоставление шаблонов в стиле оболочки). Будут загружены только те имена модулей, которые можно импортировать (т.е. являются допустимыми идентификаторами Python).
Все тестовые модули должны быть импортируемы с верхнего уровня проекта. Если начальный каталог не является каталогом верхнего уровня, то каталог верхнего уровня должен быть указан отдельно.
Если импорт модуля не удался, например, из-за синтаксической ошибки, то это будет записано как одна ошибка, и обнаружение будет продолжено. Если сбой импорта произошел из-за того, что было вызвано предупреждение
SkipTest
, то это будет записано как пропуск, а не ошибка.Если пакет (каталог, содержащий файл с именем
__init__.py
) найден, пакет будет проверен на наличие функцииload_tests
. Если такая функция существует, то она будет вызванаpackage.load_tests(loader, tests, pattern)
. Обнаружение тестов заботится о том, чтобы пакет проверялся на наличие тестов только один раз во время вызова, даже если сама функция load_tests вызываетloader.discover
.Если
load_tests
существует, то discovery нет рекурсии в пакет,load_tests
отвечает за загрузку всех тестов в пакете.Шаблон намеренно не хранится как атрибут загрузчика, чтобы пакеты могли самостоятельно продолжать открытие. top_level_dir сохраняется, поэтому
load_tests
не нужно передавать этот аргумент вloader.discover()
.start_dir может быть как точечным именем модуля, так и каталогом.
Добавлено в версии 3.2.
Изменено в версии 3.4: Модули, которые при импорте выдают
SkipTest
, записываются как пропуски, а не ошибки.Изменено в версии 3.4: Пути сортируются перед импортом, чтобы порядок выполнения был одинаковым, даже если порядок в базовой файловой системе не зависит от имени файла.
Изменено в версии 3.5: Найденные пакеты теперь проверяются на наличие
load_tests
независимо от того, соответствует ли их путь шаблону, поскольку невозможно, чтобы имя пакета соответствовало шаблону по умолчанию.
Следующие атрибуты
TestLoader
могут быть настроены либо путем подклассификации, либо назначением на экземпляр:-
testMethodPrefix
¶ -
Строка, задающая префикс имен методов, которые будут интерпретироваться как методы тестирования. По умолчанию используется значение
'test'
.Это влияет на
getTestCaseNames()
и все методыloadTestsFrom*()
.
-
sortTestMethodsUsing
¶ -
Функция, используемая для сравнения имен методов при сортировке в
getTestCaseNames()
и всех методовloadTestsFrom*()
.
-
suiteClass
¶ -
Вызываемый объект, который конструирует набор тестов из списка тестов. Никакие методы на результирующем объекте не нужны. По умолчанию используется класс
TestSuite
.Это влияет на все методы
loadTestsFrom*()
.
-
testNamePatterns
¶ -
Список шаблонов имен тестов в стиле Unix shell, которым должны соответствовать методы тестирования, чтобы быть включенными в наборы тестов (см. опцию
-v
).Если этот атрибут не равен
None
(по умолчанию), все тестовые методы, включаемые в наборы тестов, должны соответствовать одному из шаблонов в этом списке. Обратите внимание, что соответствие всегда выполняется с использованиемfnmatch.fnmatchcase()
, поэтому в отличие от шаблонов, передаваемых в опцию-v
, простые подстрочные шаблоны должны быть преобразованы с использованием*
подстановочных знаков.Это влияет на все методы
loadTestsFrom*()
.Добавлено в версии 3.7.
-
-
class
unittest.
TestResult
¶ -
Этот класс используется для сбора информации о том, какие тесты прошли успешно, а какие — неудачно.
Объект
TestResult
хранит результаты набора тестов. КлассыTestCase
иTestSuite
обеспечивают правильную запись результатов; авторам тестов не нужно беспокоиться о записи результатов тестов.Механизмы тестирования, построенные поверх
unittest
, могут захотеть получить доступ к объектуTestResult
, сгенерированному при выполнении набора тестов, для создания отчетов; для этого методомTestResult
возвращается экземплярTestRunner.run()
.TestResult
экземпляры имеют следующие атрибуты, которые будут интересны при проверке результатов выполнения набора тестов:-
errors
¶ -
Список, содержащий 2 кортежа экземпляров
TestCase
и строки, содержащие отформатированные трассировки. Каждый кортеж представляет тест, который вызвал неожиданное исключение.
-
failures
¶ -
Список, содержащий 2 кортежа экземпляров
TestCase
и строки, содержащие отформатированные трассировки. Каждый кортеж представляет тест, в котором сбой был явно сигнализирован с помощью методовTestCase.assert*()
.
-
skipped
¶ -
Список, содержащий 2 кортежа экземпляров
TestCase
и строки, содержащие причину пропуска теста.Добавлено в версии 3.1.
-
expectedFailures
¶ -
Список, содержащий 2 кортежа экземпляров
TestCase
и строки, содержащие отформатированные трассировки. Каждый кортеж представляет ожидаемый сбой или ошибку тестового примера.
-
unexpectedSuccesses
¶ -
Список, содержащий
TestCase
экземпляров, которые были помечены как ожидаемые неудачи, но завершились успешно.
-
shouldStop
¶ -
Устанавливается в
True
, когда выполнение тестов должно быть остановлено поstop()
.
-
testsRun
¶ -
Общее количество тестов, выполненных на данный момент.
-
buffer
¶ -
Если установлено значение true,
sys.stdout
иsys.stderr
будут буферизироваться между вызовамиstartTest()
иstopTest()
. Собранный вывод будет передан на реальныеsys.stdout
иsys.stderr
только в случае неудачи или ошибки теста. Любой вывод также прикрепляется к сообщению о неудаче/ошибке.Добавлено в версии 3.2.
-
failfast
¶ -
Если установлено значение true
stop()
будет вызываться при первом сбое или ошибке, останавливая выполнение теста.Добавлено в версии 3.2.
-
tb_locals
¶ -
Если установлено значение true, то локальные переменные будут показаны в трассировках.
Добавлено в версии 3.5.
-
wasSuccessful
()¶ -
Возвращает
True
, если все тесты, запущенные на данный момент, прошли, в противном случае возвращаетFalse
.Изменено в версии 3.4: Возвращает
False
, если были какие-либоunexpectedSuccesses
из тестов, помеченных декораторомexpectedFailure()
.
-
stop
()¶ -
Этот метод может быть вызван для сигнализации о том, что набор выполняемых тестов должен быть прерван путем установки атрибута
shouldStop
в значениеTrue
. ОбъектыTestRunner
должны уважать этот флаг и возвращаться без выполнения дополнительных тестов.Например, эта возможность используется классом
TextTestRunner
для остановки тестового фреймворка, когда пользователь подает сигнал прерывания с клавиатуры. Интерактивные инструменты, предоставляющие реализацииTestRunner
, могут использовать это аналогичным образом.
Следующие методы класса
TestResult
используются для поддержания внутренних структур данных и могут быть расширены в подклассах для поддержки дополнительных требований к отчетности. Это особенно полезно при создании инструментов, поддерживающих интерактивные отчеты во время выполнения тестов.-
startTest
(test)¶ -
Вызывается, когда тестовый пример test собирается быть запущенным.
-
stopTest
(test)¶ -
Вызывается после выполнения тестового случая test, независимо от результата.
-
startTestRun
()¶ -
Вызывается один раз перед выполнением любых тестов.
Добавлено в версии 3.1.
-
stopTestRun
()¶ -
Вызывается один раз после выполнения всех тестов.
Добавлено в версии 3.1.
-
addError
(test, err)¶ -
Вызывается, когда тестовый пример test вызывает неожиданное исключение. err — это кортеж формы, возвращаемой
sys.exc_info()
:(type, value, traceback)
.Реализация по умолчанию добавляет кортеж
(test, formatted_err)
к атрибутуerrors
экземпляра, где formatted_err — это форматированный откат, полученный из err.
-
addFailure
(test, err)¶ -
Вызывается, когда тестовый пример test сигнализирует о неудаче. err — это кортеж формы, возвращаемой
sys.exc_info()
:(type, value, traceback)
.Реализация по умолчанию добавляет кортеж
(test, formatted_err)
к атрибутуfailures
экземпляра, где formatted_err — это форматированный откат, полученный из err.
-
addSuccess
(test)¶ -
Вызывается при успешном выполнении тестового случая test.
Реализация по умолчанию ничего не делает.
-
addSkip
(test, reason)¶ -
Вызывается, когда тест test пропущен. причина — это причина пропуска теста.
Реализация по умолчанию добавляет кортеж
(test, reason)
к атрибуту экземпляраskipped
.
-
addExpectedFailure
(test, err)¶ -
Вызывается, когда тестовый пример test провалился или ошибся, но был помечен декоратором
expectedFailure()
.Реализация по умолчанию добавляет кортеж
(test, formatted_err)
к атрибутуexpectedFailures
экземпляра, где formatted_err — это форматированный откат, полученный из err.
-
addUnexpectedSuccess
(test)¶ -
Вызывается, если тестовый пример test был помечен декоратором
expectedFailure()
, но прошел успешно.Реализация по умолчанию добавляет тест к атрибуту
unexpectedSuccesses
экземпляра.
-
addSubTest
(test, subtest, outcome)¶ -
Вызывается при завершении подтеста. test — это тестовый пример, соответствующий методу тестирования. subtest — пользовательский экземпляр
TestCase
, описывающий подтест.Если outcome равен
None
, подтест прошел успешно. В противном случае он завершился с исключением, где outcome является кортежем формы, возвращаемой командойsys.exc_info()
:(type, value, traceback)
.Реализация по умолчанию ничего не делает, если результат успешный, и записывает неудачи подтеста как обычные неудачи.
Добавлено в версии 3.4.
-
-
class
unittest.
TextTestResult
(stream, descriptions, verbosity)¶ -
Конкретная реализация
TestResult
, используемаяTextTestRunner
.Добавлено в версии 3.2: Ранее этот класс назывался
_TextTestResult
. Старое имя все еще существует как псевдоним, но оно устарело.
-
unittest.
defaultTestLoader
¶ -
Экземпляр класса
TestLoader
, предназначенный для совместного использования. Если не требуется настройкаTestLoader
, этот экземпляр можно использовать вместо многократного создания новых экземпляров.
-
class
unittest.
TextTestRunner
(stream=None, descriptions=True, verbosity=1, failfast=False, buffer=False, resultclass=None, warnings=None, *, tb_locals=False)¶ -
Базовая реализация программы запуска тестов, которая выводит результаты в поток. Если stream равен
None
, то по умолчанию в качестве выходного потока используетсяsys.stderr
. Этот класс имеет несколько настраиваемых параметров, но по сути очень прост. Графические приложения, запускающие тестовые наборы, должны предоставлять альтернативные реализации. Такие реализации должны принимать**kwargs
в качестве интерфейса для построения бегунков, изменяющихся при добавлении функций в unittest.По умолчанию этот бегунок показывает
DeprecationWarning
,PendingDeprecationWarning
,ResourceWarning
иImportWarning
, даже если они ignored by default. Предупреждения об износе, вызванные deprecated unittest methods, также имеют особый случай, и, если фильтры предупреждений'default'
или'always'
, они будут появляться только один раз для каждого модуля, чтобы избежать слишком большого количества предупреждений. Это поведение можно отменить, используя опции Python-Wd
или-Wa
(см. Warning control) и оставив warnings вNone
.Изменено в версии 3.2: Добавлен аргумент
warnings
.Изменено в версии 3.2: Поток по умолчанию устанавливается в
sys.stderr
во время инстанцирования, а не во время импорта.Изменено в версии 3.5: Добавлен параметр tb_locals.
-
_makeResult
()¶ -
Этот метод возвращает экземпляр
TestResult
, используемыйrun()
. Он не предназначен для прямого вызова, но может быть переопределен в подклассах для обеспечения пользовательскогоTestResult
._makeResult()
инстанцирует класс или вызываемый объект, переданный в конструктореTextTestRunner
в качестве аргументаresultclass
. По умолчанию используетсяTextTestResult
, если не указаноresultclass
. Класс result инстанцируется со следующими аргументами:stream, descriptions, verbosity
-
run
(test)¶ -
Этот метод является основным публичным интерфейсом для
TextTestRunner
. Этот метод принимает экземплярTestSuite
илиTestCase
.TestResult
создается вызовом_makeResult()
, выполняется тест(ы) и результаты выводятся в stdout.
-
-
unittest.
main
(module=‘__main__’, defaultTest=None, argv=None, testRunner=None, testLoader=unittest.defaultTestLoader, exit=True, verbosity=1, failfast=None, catchbreak=None, buffer=None, warnings=None)¶ -
Программа командной строки, которая загружает набор тестов из модуля и запускает их; в первую очередь это нужно для того, чтобы сделать тестовые модули удобно исполняемыми. Самое простое использование этой функции — включить следующую строку в конец тестового скрипта:
if __name__ == '__main__': unittest.main()
Вы можете запускать тесты с более подробной информацией, передавая аргумент verbosity:
if __name__ == '__main__': unittest.main(verbosity=2)
Аргумент defaultTest является либо именем одного теста, либо итерацией имен тестов для запуска, если имена тестов не указаны через argv. Если аргумент не указан или
None
и имена тестов не указаны через argv, запускаются все тесты, найденные в module.Аргумент argv может представлять собой список опций, передаваемых программе, первым элементом которого является имя программы. Если он не указан или
None
, то используются значения изsys.argv
.Аргумент testRunner может быть либо классом тестового бегуна, либо уже созданным его экземпляром. По умолчанию
main
вызываетsys.exit()
с кодом выхода, указывающим на успех или неудачу выполнения тестов.Аргумент testLoader должен быть экземпляром
TestLoader
и по умолчанию имеет значениеdefaultTestLoader
.main
поддерживает использование из интерактивного интерпретатора путем передачи аргументаexit=False
. Это выводит результат на стандартный вывод без вызоваsys.exit()
:>>> from unittest import main >>> main(module='test_module', exit=False)
Параметры failfast, catchbreak и buffer имеют тот же эффект, что и одноименные command-line options.
Аргумент warnings задает warning filter, который должен использоваться при выполнении тестов. Если он не указан, то останется
None
, если в-W
передана опция python (см. Warning control), иначе будет установлено значение'default'
.Вызов
main
фактически возвращает экземпляр классаTestProgram
. В нем хранится результат выполнения тестов в виде атрибутаresult
.Изменено в версии 3.1: Был добавлен параметр exit.
Изменено в версии 3.2: Добавлены параметры verbosity, failfast, catchbreak, buffer и warnings.
Изменено в версии 3.4: Параметр defaultTest был изменен, чтобы также принимать итерабельность имен тестов.
load_tests Протокол¶
Добавлено в версии 3.2.
Модули или пакеты могут настроить загрузку тестов из них во время обычного запуска тестов или обнаружения тестов, реализовав функцию load_tests
.
Если тестовый модуль определяет load_tests
, он будет вызван TestLoader.loadTestsFromModule()
со следующими аргументами:
load_tests(loader, standard_tests, pattern)
где шаблон передается прямо из loadTestsFromModule
. По умолчанию он равен None
.
Он должен вернуть значение TestSuite
.
loader — это экземпляр TestLoader
, выполняющий загрузку. standard_tests — это тесты, которые будут загружаться по умолчанию из модуля. Обычно модули тестирования хотят только добавлять или удалять тесты из стандартного набора тестов. Третий аргумент используется при загрузке пакетов как часть обнаружения тестов.
Типичная функция load_tests
, которая загружает тесты из определенного набора TestCase
классов, может выглядеть так:
test_cases = (TestCase1, TestCase2, TestCase3) def load_tests(loader, tests, pattern): suite = TestSuite() for test_class in test_cases: tests = loader.loadTestsFromTestCase(test_class) suite.addTests(tests) return suite
Если discovery запускается в каталоге, содержащем пакет, либо из командной строки, либо вызовом TestLoader.discover()
, то пакет __init__.py
будет проверен на наличие load_tests
. Если такой функции не существует, discovery выполнит поиск в пакете, как если бы это был просто другой каталог. В противном случае обнаружение тестов пакета будет возложено на функцию load_tests
, которая вызывается со следующими аргументами:
load_tests(loader, standard_tests, pattern)
Это должно вернуть TestSuite
, представляющий все тесты из пакета. (standard_tests
будет содержать только тесты, собранные из __init__.py
).
Поскольку шаблон передается в load_tests
, пакет может продолжать (и потенциально изменять) обнаружение теста. Функция «ничего не делать» load_tests
для тестового пакета будет выглядеть следующим образом:
def load_tests(loader, standard_tests, pattern): # top level directory cached on loader instance this_dir = os.path.dirname(__file__) package_tests = loader.discover(start_dir=this_dir, pattern=pattern) standard_tests.addTests(package_tests) return standard_tests
Изменено в версии 3.5: Discovery больше не проверяет имена пакетов на соответствие шаблону из-за невозможности совпадения имен пакетов с шаблоном по умолчанию.
Приспособления для классов и модулей¶
Фиксы на уровне классов и модулей реализованы в TestSuite
. Когда набор тестов встречает тест из нового класса, то вызывается tearDownClass()
из предыдущего класса (если он есть), а затем setUpClass()
из нового класса.
Аналогично, если тест из другого модуля, чем предыдущий тест, то запускается tearDownModule
из предыдущего модуля, а затем setUpModule
из нового модуля.
После выполнения всех тестов запускаются финальные tearDownClass
и tearDownModule
.
Обратите внимание, что общие фикстуры плохо сочетаются с такими [потенциальными] возможностями, как распараллеливание тестов, и нарушают изоляцию тестов. Их следует использовать с осторожностью.
Упорядочивание тестов, создаваемых загрузчиками тестов unittest, по умолчанию заключается в том, чтобы сгруппировать все тесты из одних и тех же модулей и классов вместе. Это приведет к тому, что setUpClass
/ setUpModule
(и т.д.) будут вызываться ровно один раз для каждого класса и модуля. Если вы измените порядок, чтобы тесты из разных модулей и классов располагались рядом друг с другом, то эти общие функции приспособления могут быть вызваны несколько раз за один прогон теста.
Общие фикстуры не предназначены для работы с наборами с нестандартным упорядочиванием. Для фреймворков, которые не хотят поддерживать общие приспособления, по-прежнему существует BaseTestSuite
.
Если во время выполнения одной из общих функций приспособления возникают какие-либо исключения, тест сообщается как ошибка. Поскольку нет соответствующего экземпляра теста, создается объект _ErrorHolder
(который имеет тот же интерфейс, что и TestCase
) для представления ошибки. Если вы просто используете стандартную программу запуска тестов unittest, то эта деталь не имеет значения, но если вы являетесь автором фреймворка, то она может иметь значение.
setUpClass и tearDownClass¶
Они должны быть реализованы как методы класса:
import unittest class Test(unittest.TestCase): @classmethod def setUpClass(cls): cls._connection = createExpensiveConnectionObject() @classmethod def tearDownClass(cls): cls._connection.destroy()
Если вы хотите, чтобы вызывались setUpClass
и tearDownClass
на базовых классах, то вы должны вызвать их сами. Реализации в TestCase
являются пустыми.
Если во время setUpClass
возникает исключение, то тесты в классе не выполняются, а tearDownClass
не запускается. У пропущенных классов не будут выполняться setUpClass
или tearDownClass
. Если исключение является исключением SkipTest
, то класс будет сообщен как пропущенный, а не как ошибка.
setUpModule и tearDownModule¶
Они должны быть реализованы как функции:
def setUpModule(): createConnection() def tearDownModule(): closeConnection()
Если в setUpModule
возникло исключение, то ни один из тестов модуля не будет выполнен, а tearDownModule
не будет выполнен. Если исключение является исключением SkipTest
, то модуль будет сообщен как пропущенный, а не как ошибка.
Чтобы добавить код очистки, который должен быть выполнен даже в случае исключения, используйте addModuleCleanup
:
-
unittest.
addModuleCleanup
(function, /, *args, **kwargs)¶ -
Добавьте функцию, которая будет вызываться после
tearDownModule()
для очистки ресурсов, использованных во время работы тестового класса. Функции будут вызываться в порядке, обратном порядку их добавления (LIFO). Они вызываются с любыми аргументами и аргументами ключевых слов, переданными вaddModuleCleanup()
при их добавлении.Если
setUpModule()
потерпит неудачу, то естьtearDownModule()
не будет вызван, то все добавленные функции очистки все равно будут вызваны.Добавлено в версии 3.8.
-
unittest.
doModuleCleanups
()¶ -
Эта функция вызывается безусловно после
tearDownModule()
, или послеsetUpModule()
, еслиsetUpModule()
вызывает исключение.Он отвечает за вызов всех функций очистки, добавленных
addModuleCleanup()
. Если вам нужно, чтобы функции очистки вызывались приоритетно кtearDownModule()
, то вы можете вызватьdoModuleCleanups()
самостоятельно.doModuleCleanups()
выгружает методы из стека функций очистки по одному за раз, поэтому его можно вызвать в любое время.Добавлено в версии 3.8.
Обработка сигналов¶
Добавлено в версии 3.2.
Опция командной строки -c/--catch
для unittest, а также параметр catchbreak
для unittest.main()
обеспечивают более дружественное обращение с control-C во время выполнения теста. При включенном поведении catch break control-C позволит завершить текущий тест, после чего тест завершится и сообщит обо всех полученных результатах. Повторное нажатие control-C вызовет ошибку KeyboardInterrupt
обычным способом.
Обработчик сигналов control-c пытается сохранить совместимость с кодом или тестами, которые устанавливают свой собственный обработчик signal.SIGINT
. Если вызывается обработчик unittest
, но он не является установленным обработчиком signal.SIGINT
, т.е. он был заменен тестируемой системой и делегирован ей, то вызывается обработчик по умолчанию. Обычно это будет ожидаемым поведением кода, который заменяет установленный обработчик и делегирует ему полномочия. Для отдельных тестов, в которых требуется отключить обработку unittest
control-c, можно использовать декоратор removeHandler()
.
Существует несколько полезных функций для авторов фреймворков, позволяющих включить функциональность обработки control-c в тестовые фреймворки.
-
unittest.
installHandler
()¶ -
Установите обработчик control-c. При получении
signal.SIGINT
(обычно в ответ на нажатие пользователем кнопки control-c) у всех зарегистрированных результатов вызываетсяstop()
.
-
unittest.
registerResult
(result)¶ -
Зарегистрируйте объект
TestResult
для обработки контрола-c. Регистрация результата хранит слабую ссылку на него, поэтому она не предотвращает сборку результата в мусор.Регистрация объекта
TestResult
не имеет побочных эффектов, если обработка control-c не включена, поэтому тестовые системы могут безоговорочно регистрировать все создаваемые ими результаты независимо от того, включена обработка или нет.
-
unittest.
removeResult
(result)¶ -
Удаление зарегистрированного результата. Если результат был удален, то
stop()
больше не будет вызываться на этом объекте результата в ответ на команду control-c.
-
unittest.
removeHandler
(function=None)¶ -
При вызове без аргументов эта функция удаляет обработчик control-c, если он был установлен. Эта функция также может использоваться в качестве декоратора теста для временного удаления обработчика на время выполнения теста:
@unittest.removeHandler def test_signal_handling(self): ...