Testing Your Code

https://d33wubrfki0l68.cloudfront.net/467452f32ed03aac2095f737b3c5d92a9b3d37bd/b2542/_images/34435687940_8f73fc1fa6_k_d.jpg
Testing your code is very important.

Getting used to writing testing code and running this code in parallel is nowconsidered a good habit. Used wisely, this method helps you define moreprecisely your code’s intent and have a more decoupled architecture.

Some general rules of testing:

  • A testing unit should focus on one tiny bit of functionality and prove itcorrect.
  • Each test unit must be fully independent. Each test must be able to runalone, and also within the test suite, regardless of the order that they arecalled. The implication of this rule is that each test must be loaded witha fresh dataset and may have to do some cleanup afterwards. This isusually handled by setUp() and tearDown() methods.
  • Try hard to make tests that run fast. If one single test needs more than afew milliseconds to run, development will be slowed down or the tests willnot be run as often as is desirable. In some cases, tests can’t be fastbecause they need a complex data structure to work on, and this data structuremust be loaded every time the test runs. Keep these heavier tests in aseparate test suite that is run by some scheduled task, and run all othertests as often as needed.
  • Learn your tools and learn how to run a single test or a test case. Then,when developing a function inside a module, run this function’s testsfrequently, ideally automatically when you save the code.
  • Always run the full test suite before a coding session, and run it againafter. This will give you more confidence that you did not break anythingin the rest of the code.
  • It is a good idea to implement a hook that runs all tests before pushingcode to a shared repository.
  • If you are in the middle of a development session and have to interruptyour work, it is a good idea to write a broken unit test about what youwant to develop next. When coming back to work, you will have a pointerto where you were and get back on track faster.
  • The first step when you are debugging your code is to write a new testpinpointing the bug. While it is not always possible to do, those bugcatching tests are among the most valuable pieces of code in your project.
  • Use long and descriptive names for testing functions. The style guide hereis slightly different than that of running code, where short names areoften preferred. The reason is testing functions are never called explicitly.square() or even sqr() is ok in running code, but in testing code youwould have names such as test_square_of_number_2(),test_square_negative_number(). These function names are displayed whena test fails, and should be as descriptive as possible.
  • When something goes wrong or has to be changed, and if your code has agood set of tests, you or other maintainers will rely largely on thetesting suite to fix the problem or modify a given behavior. Thereforethe testing code will be read as much as or even more than the runningcode. A unit test whose purpose is unclear is not very helpful in thiscase.
  • Another use of the testing code is as an introduction to new developers. Whensomeone will have to work on the code base, running and reading the relatedtesting code is often the best thing that they can do to start. They willor should discover the hot spots, where most difficulties arise, and thecorner cases. If they have to add some functionality, the first step shouldbe to add a test to ensure that the new functionality is not already aworking path that has not been plugged into the interface.

The Basics

unittest

unittest is the batteries-included test module in the Python standardlibrary. Its API will be familiar to anyone who has used any of theJUnit/nUnit/CppUnit series of tools.

Creating test cases is accomplished by subclassing unittest.TestCase.

  1. import unittest
  2.  
  3. def fun(x):
  4. return x + 1
  5.  
  6. class MyTest(unittest.TestCase):
  7. def test(self):
  8. self.assertEqual(fun(3), 4)

As of Python 2.7 unittest also includes its own test discovery mechanisms.

unittest in the standard library documentation

Doctest

The doctest module searches for pieces of text that look like interactivePython sessions in docstrings, and then executes those sessions to verify thatthey work exactly as shown.

Doctests have a different use case than proper unit tests: they are usuallyless detailed and don’t catch special cases or obscure regression bugs. Theyare useful as an expressive documentation of the main use cases of a module andits components. However, doctests should run automatically each time the fulltest suite runs.

A simple doctest in a function:

  1. def square(x):
  2. """Return the square of x.
  3.  
  4. >>> square(2)
  5. 4
  6. >>> square(-2)
  7. 4
  8. """
  9.  
  10. return x * x
  11.  
  12. if __name__ == '__main__':
  13. import doctest
  14. doctest.testmod()

When running this module from the command line as in python module.py, thedoctests will run and complain if anything is not behaving as described in thedocstrings.

Tools

py.test

py.test is a no-boilerplate alternative to Python’s standard unittest module.

  1. $ pip install pytest

Despite being a fully-featured and extensible test tool, it boasts a simplesyntax. Creating a test suite is as easy as writing a module with a couple offunctions:

  1. # content of test_sample.py
  2. def func(x):
  3. return x + 1
  4.  
  5. def test_answer():
  6. assert func(3) == 5

and then running the py.test command:

  1. $ py.test
  2. =========================== test session starts ============================
  3. platform darwin -- Python 2.7.1 -- pytest-2.2.1
  4. collecting ... collected 1 items
  5.  
  6. test_sample.py F
  7.  
  8. ================================= FAILURES =================================
  9. _______________________________ test_answer ________________________________
  10.  
  11. def test_answer():
  12. > assert func(3) == 5
  13. E assert 4 == 5
  14. E + where 4 = func(3)
  15.  
  16. test_sample.py:5: AssertionError
  17. ========================= 1 failed in 0.02 seconds =========================

is far less work than would be required for the equivalent functionality withthe unittest module!

py.test

Hypothesis

Hypothesis is a library which lets you write tests that are parametrized bya source of examples. It then generates simple and comprehensible examplesthat make your tests fail, letting you find more bugs with less work.

  1. $ pip install hypothesis

For example, testing lists of floats will try many examples, but report theminimal example of each bug (distinguished exception type and location):

  1. @given(lists(floats(allow_nan=False, allow_infinity=False), min_size=1))
  2. def test_mean(xs):
  3. mean = sum(xs) / len(xs)
  4. assert min(xs) <= mean(xs) <= max(xs)
  1. Falsifying example: test_mean(
  2. xs=[1.7976321109618856e+308, 6.102390043022755e+303]
  3. )

Hypothesis is practical as well as very powerful and will often find bugsthat escaped all other forms of testing. It integrates well with py.test,and has a strong focus on usability in both simple and advanced scenarios.

hypothesis

tox

tox is a tool for automating test environment management and testing againstmultiple interpreter configurations.

  1. $ pip install tox

tox allows you to configure complicated multi-parameter test matrices via asimple INI-style configuration file.

tox

Unittest2

unittest2 is a backport of Python 2.7’s unittest module which has an improvedAPI and better assertions over the one available in previous versions of Python.

If you’re using Python 2.6 or below, you can install it with pip:

  1. $ pip install unittest2

You may want to import the module under the name unittest to make porting codeto newer versions of the module easier in the future

  1. import unittest2 as unittest
  2.  
  3. class MyTest(unittest.TestCase):
  4. ...

This way if you ever switch to a newer Python version and no longer need theunittest2 module, you can simply change the import in your test module withoutthe need to change any other code.

unittest2

mock

unittest.mock is a library for testing in Python. As of Python 3.3, it isavailable in thestandard library.

For older versions of Python:

  1. $ pip install mock

It allows you to replace parts of your system under test with mock objects andmake assertions about how they have been used.

For example, you can monkey-patch a method:

  1. from mock import MagicMock
  2. thing = ProductionClass()
  3. thing.method = MagicMock(return_value=3)
  4. thing.method(3, 4, 5, key='value')
  5.  
  6. thing.method.assert_called_with(3, 4, 5, key='value')

To mock classes or objects in a module under test, use the patch decorator.In the example below, an external search system is replaced with a mock thatalways returns the same result (but only for the duration of the test).

  1. def mock_search(self):
  2. class MockSearchQuerySet(SearchQuerySet):
  3. def __iter__(self):
  4. return iter(["foo", "bar", "baz"])
  5. return MockSearchQuerySet()
  6.  
  7. # SearchForm here refers to the imported class reference in myapp,
  8. # not where the SearchForm class itself is imported from
  9. @mock.patch('myapp.SearchForm.search', mock_search)
  10. def test_new_watchlist_activities(self):
  11. # get_search_results runs a search and iterates over the result
  12. self.assertEqual(len(myapp.get_search_results(q="fish")), 3)

Mock has many other ways you can configure it and control its behavior.

mock

原文: https://docs.python-guide.org/writing/tests/