Click here for the first post, which contains the context of this series.
Item #76: Verify related behaviors in TestCase subclasses.
- You can create tests by subclassing the TestCase class from the unittest built-in module and defining one method per behavior you'd like to test. Test methods on TestCase classes must start with the word test.
- Use the various helper methods defined by the TestCase class, such as assertEqual, to confirm expected behaviors in your tests instead of using the built-in assert statement.
- Consider writing data-driven tests using the subTest helper method in order to reduce boilerplate.
Item #77: Isolate tests from each other with setUp, tearDown, setUpModule, and tearDownModule.
- It's important to write both unit tests (for isolated functionality) and integration tests (for modules that interact with each other).
- Use the setUp and tearDown methods to make sure your tests are isolated from each other and have a clean test environment.
- For integration tests, use the setUpModule and tearDownModule module-level functions to manage any test harnesses you need for the entire lifetime of a test module and all of the TestCase classes that it contains.
Item #78: Use mocks to test code with complex dependencies.
- The unittest.mock module provides a way to simulate the behavior of interfaces using the Mock class. Mocks are useful in tests when it's difficult to set up the dependencies that are required by the code that's being tested.
- When using mocks, it's important to verify both the behavior of the code being tested and how dependent functions were called by that code, using the Mock.assert_called_once_with family of methods.
- Keyword-only arguments and the unittest.mock.patch family of functions can be used to inject mocks into the code being tested.
Item #79: Encapsulate dependencies to facilitate mocking and testing.
- When unit tests require a lot of repeated boilerplate to set up mocks, one solution may be to encapsulate the functionality of dependencies into classes that are more easily mocked.
- The Mock class of the unittest.mock built-in module simulates classes by returning a new mock, which can act as a mock method, for each attribute that is accessed.
- For end-to-end tests, it's valuable to refactor your code to have more helper functions that can act as explicit seams to use for injecting mock dependencies in tests.
Item #80: Consider interactive debugging with pdb.
- You can initiate the Python interactive debugger at a point of interest directly in your program by calling the breakpoint built-in function.
- The Python debugger prompt is a full Python shell that lets you inspect and modify the state of a running program.
- pdb shell commands let you precisely control program execution and allow you to alternate between inspecting program state and progressing program execution.
- The pdb module can be used for debug exceptions after they happen in independent Python programs (using python -m pdb -c continue <program path>) or the interactive Python interpreter (using import pdb; pdb.pm()).