Coding With Fun
Home Docker Django Node.js Articles Python pip guide FAQ Policy

There are several common test frameworks for Python


May 30, 2021 Article blog


Table of contents


Common rules for testing

  1. A test unit must pay attention to a small function function to prove that it is correct;
  2. Each test unit must be completely independent and must be able to run separately. T his means that each test method must reload the data and do some cleanup after execution. Usually handled by setUp() and setDown() methods;
  3. Write test code that executes quickly. I n some cases, testing requires loading complex data structures and reloading every time it is performed, which can be slow to execute. Therefore, in this case, you can place this test in a background task.
  4. Use test tools and learn how to use them.
  5. Perform the complete test before writing the code, and then re-execute it again after you write the code. This ensures that the code you write later doesn't break anything;
  6. Perform a complete test before submitting the code;
  7. If you are interrupted during development, write an interrupted unit test about the next step you will develop. When you come back to work, you can know the pointers that were developed in the previous step;
  8. Unit test functions use long, descriptive names. In formal execution code, you might use square() or sqr(), but in a test function, you have to take names like test_square_of_number_2 (), test_square_negativer_number (), which are more clearly described;
  9. The test code must be readable;
  10. Unit testing is a working guide for new developers.

The purpose of unit testing is to verify the correctness of a module, a function, or a class, and if the unit test passes, the object we are testing works. I f the unit test fails, either the test object has a bug or the test condition is not entered correctly. Here's a small compilation to introduce several test frameworks for Python.

Recommended lessons: Python Automation Management, Python Automation Office.

1. unittest

Unittest, like JUnit, can be said to be python's standard unit test framework, so it is sometimes referred to as PyUnit. I t works like other members of the xUnit family. T here are also more people using it. Compatible with python2 and python3.

Personally prefer to use this, mainly before using JUnit, with this quickly. And belongs to python automatic integration, do not use the additional installation package, feel that there should be, easy to use.

example:

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()

2. unittest2

Unittest2 can be said to be a patch for the new features of the unittest test framework. I t's largely similar to unittest. T hen you add some methods that unittest doesn't have.

3. pytest

Reference document: http://pytest.org/latest/

After a look, the pytest document is still quite detailed. A closer concern is that pytest can be parameterized directly through @pytest.mark.parametrize, whereas unittest requires DDT.

example:

def inc(x):

return x + 1

def test_answer():

assert inc(3) == 5

The execution is as follows:

$ pytest
======= test session starts ========
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
rootdir: $REGENDOC_TMPDIR, inifile:
collected 1 item
test_sample.py F
======= FAILURES ========
_______ test_answer ________
def test_answer():
> assert inc(3) == 5
E assert 4 == 5
E + where 4 = inc(3)
test_sample.py:5: AssertionError
======= 1 failed in 0.12 seconds ========

4. nose

nose extends unittest to make testing easier.

Use cases can generally be written as unittest and executed with nose after writing. Nose's test collection is still convenient.

Another particular is that nose can use @with_setup() to define setup and teardown for methods.

example:

def setup_func():

"set up test fixtures"

def teardown_func():

"tear down test fixtures"

@with_setup(setup_func, teardown_func)

def test():

"test ..."

5. doctest

The doctest module searches for Python snippets that look like interactive sessions, and then tries to execute and validate the results.

In doctest, if you want to write a test case, you only need to write comments on the document that is surrounded by ', which is where the property can be referenced by the __doc__. T his is special, unlike other unit test frameworks. But I think that's what makes doctest unsuitable for large-scale testing, because it doesn't separate code from testing.

import doctest

"""

This is the "example" module.

The example module supplies one function, factorial().  For example,

>>> factorial(5)

120

"""

def factorial(n):

"""Return the factorial of n, an exact integer >= 0.

>>> [factorial(n) for n in range(6)]

[1, 1, 2, 6, 24, 120]

>>> factorial(30)

265252859812191058636308480000000

>>> factorial(-1)

Traceback (most recent call last):

...

ValueError: n must be >= 0

Factorials of floats are OK, but the float must be an exact integer:

>>> factorial(30.1)

Traceback (most recent call last):

...

ValueError: n must be exact integer

>>> factorial(30.0)

265252859812191058636308480000000

It must also not be ridiculously large:

>>> factorial(1e100)

Traceback (most recent call last):

...

OverflowError: n too large

"""

import math

if not n >= 0:

raise ValueError("n must be >= 0")

if math.floor(n) != n:

raise ValueError("n must be exact integer")

if n+1 == n:  # catch a value like 1e300

raise OverflowError("n too large")

result = 1

factor = 2

while factor <= n:

result *= factor

factor += 1

return result

if __name__ == "__main__":

doctest.testmod(verbose=True)

The verbose parameter is used to control whether to output details, defaulting to False, and if not written, the runtime does not output anything unless fail is tested.

The output is as follows:

Trying:
[factorial(n) for n in range(6)]
Expecting:
[1, 1, 2, 6, 24, 120]
ok
Trying:
factorial(30)
Expecting:
265252859812191058636308480000000
ok
Trying:
factorial(-1)
Expecting:
Traceback (most recent call last):
...
ValueError: n must be >= 0
ok
Trying:
factorial(30.1)
Expecting:
Traceback (most recent call last):
...
ValueError: n must be exact integer
ok
Trying:
factorial(30.0)
Expecting:
265252859812191058636308480000000
ok
Trying:
factorial(1e100)
Expecting:
Traceback (most recent call last):
...
OverflowError: n too large
ok
1 items had no tests:
__main__
1 items passed all tests:
6 tests in __main__.factorial
6 tests in 2 items.
6 passed and 0 failed.
Test passed.