Unittest assert error

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

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 by callable.
  • callable – Function to be called. A string identifying a callable object may also be supplied. The callable will be invoked with args and kwds as appropriate.
  • *args – Positional arguments passed to callable when invoked.
  • **kwds – Keyword arguments passed to callable 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:

Python Unittest assertRaises Output
Output

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
by unittest.
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 second Control-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 the unittest 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 of TestCase, you will neither change
the methodName nor reimplement the default runTest() method.

Changed in version 3.2: TestCase can be instantiated successfully without providing a
methodName. This makes it easier to experiment with TestCase
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 than AssertionError or SkipTest,
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 or SkipTest, 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
the setUp() 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 a classmethod():

@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 a classmethod():

@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 or None, a temporary
result object is created (by calling the defaultTestResult()
method) and used. The result object is returned to run()‘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 also longMessage).
Note that the msg keyword argument can be passed to assertRaises(),
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 with addTypeEqualityFunc() 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 to expr
is True
(use assertIs(expr, True) for the latter). This method
should also be avoided when more specific methods are available (e.g.
assertEqual(a, b) instead of assertTrue(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 by isinstance()).
To check for the exact type, use assertIs(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 exc  
assertRaisesRegex(exc, r, fun, *args, **kwds) fun(*args, **kwds) raises exc
and the message matches regex r
3.1
assertWarns(warn, fun, *args, **kwds) fun(*args, **kwds) raises warn 3.2
assertWarnsRegex(warn, r, fun, *args, **kwds) fun(*args, **kwds) raises warn
and the message matches regex r
3.2
assertLogs(logger, level) The with block logs on logger
with minimum level
3.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 by re.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 the filename and lineno 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
by re.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 is logging.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 order
3.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 the round() 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 by re.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
for assertNotRegex().

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 using addTypeEqualityFunc():

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 as assertEqual() 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 with assertEqual().

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 implement assertListEqual() 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 with assertEqual().

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 to assertEqual().

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 to False, the custom message replaces the standard message.

The class setting can be overridden in individual test methods by assigning
an instance attribute, self.longMessage, to True or False 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 to None 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 be 1.

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 of TestCase 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,
or None.

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 that tearDown() 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
after setUp() if setUp() raises an exception.

It is responsible for calling all the cleanup functions added by
addCleanup(). If you need cleanup functions to be called
prior to tearDown() then you can call doCleanups()
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 and assertRaisesRegexp have been renamed to
assertRegex() and assertRaisesRegex().

Deprecated since version 3.5: the assertNotRegexpMatches name in favor of assertNotRegex().

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 a TestSuite 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 like TestCase 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 to TestSuite instances:

addTest(test)

Add a TestCase or TestSuite to the suite.

addTests(tests)

Add all the tests from an iterable of TestCase and TestSuite
instances to this test suite.

This is equivalent to iterating over tests, calling addTest() for
each element.

TestSuite shares the following methods with TestCase:

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 before TestSuite.run() must be the
same for each call iteration. After TestSuite.run(), callers should
not rely on the tests returned by this method unless the caller uses a
subclass that overrides TestSuite._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 after TestSuite.run(). Subclasses can restore
that behavior by overriding TestSuite._removeTestAtIndex().

In the typical usage of a TestSuite object, the run() method
is invoked by a TestRunner 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 with test. If getTestCaseNames() returns no
methods, but the runTest() 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 from TestCase 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 to load_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 to load_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 or TestSuite 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 class SampleTestCase with three test
methods (test_one(), test_two(), and test_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 the test_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 or AttributeError 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 of TestCase.

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 to SkipTest 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 a load_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 the loadTestsFrom*()
methods.

sortTestMethodsUsing

Function to be used to compare method names when sorting them in
getTestCaseNames() and all the loadTestsFrom*() 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 and TestSuite 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; a TestResult 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 the TestCase.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 by stop().

testsRun

The total number of tests run so far.

buffer

If set to true, sys.stdout and sys.stderr will be buffered in between
startTest() and stopTest() being called. Collected output will
only be echoed onto the real sys.stdout and sys.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 any unexpectedSuccesses
from tests marked with the expectedFailure() decorator.

stop()

This method can be called to signal that the set of tests being run should
be aborted by setting the shouldStop attribute to True.
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 provide TestRunner
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 by sys.exc_info(): (type, value,
traceback)
.

The default implementation appends a tuple (test, formatted_err) to
the instance’s errors 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 by sys.exc_info(): (type, value, traceback).

The default implementation appends a tuple (test, formatted_err) to
the instance’s failures 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’s skipped 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’s expectedFailures 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 by sys.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 the TestLoader 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
is None, 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 to None.

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 by run().
It is not intended to be called directly, but can be overridden in
subclasses to provide a custom TestResult.

_makeResult() instantiates the class or callable passed in the
TextTestRunner constructor as the resultclass argument. It
defaults to TextTestResult if no resultclass 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 a TestSuite or TestCase 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 or None 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 or None,
the values of sys.argv are used.

The testRunner argument can either be a test runner class or an already
created instance of it. By default main calls sys.exit() with
an exit code indicating success or failure of the tests run.

The testLoader argument has to be a TestLoader instance,
and defaults to defaultTestLoader.

main supports being used from the interactive interpreter by passing in the
argument exit=False. This displays the result on standard output without
calling sys.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
remain None 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 the TestProgram class.
This stores the result of the tests run as the result 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
have stop() 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 class
  • assert* 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 class
  • assert* 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 r

3.1

assertWarns(warn, fun, *args, **kwds)

fun(*args, **kwds) вызывает warn.

3.2

assertWarnsRegex(warn, r, fun, *args, **kwds)

fun(*args, **kwds) вызывает предупреждение warn и сообщение соответствует regex r

3.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"].

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):
    ...

Понравилась статья? Поделить с друзьями:
  • Unitiplayer dll ошибка
  • Unknown error 0000 router scan
  • Unknown envelope error e0031 что это
  • Unknown download error please reinstall from google play что делать
  • Unknown download error please reinstall from google play quit game retry