Fake It Til You Make It: Unit Testing Patterns With Mocks And Fakes by Brian K. Jones¶
Presenters: Brian K. Jones
PyCon 2012 presentation page: https://us.pycon.org/2012/schedule/presentation/336/
Video running time: 49:33
What is a “unit test”?¶
- A test that exercises a very small amount of code which is completely isolated from any and all dependencies, external or internal
- Granular – only testing a small piece of code
- Localized – tells you exactly where your bug is
When it is no longer a unit test?¶
- When something else in the larger system, besides the code under test, has to work.
What is it then?¶
- If multiple components of the same class or system are tested, and the test seeks to determine that these components interact in a predictable way, it’s an integration test.
- If the entire system is being tested to insure compliance with a spec of some kind, it’s an acceptance test.
What is “coverage”?¶
- A loaded term
- Often generically referred to as “code coverage”.
- What you really want is “condition coverage”, “case coverage”, “logic coverage”, “decision-point coverage”, etc.
- Integrates well with nosetests
- Super easy to use
- Can report on branch coverage using --branch
- Can be used easily with tox, by itself or from within nosetests
- Nice work! Thanks, Ned!
Speaking of nosetests¶
- Nose is a great test discovery tool
- cd to your test directory and run “nosetests”. Rejoice.
- nosetests --with-coverage (coverage.py is a nose plugin!)
- Has a super nice HTML report that highlights covered/uncovered lines
- Integrates well with Jenkins to present html and provide pretty graphs (managers like those):
Why unit tests?¶
- They’re fast
- They’re simple
- They provide greater localization of problems than other types of tests often can
Unit tests aren’t enough¶
Unit tests don’t test integration - that the parts of the system all work together
Mock is cool. Use it.¶
- It patches all the things
- Often used as a “spy” library (technically)
- I use it so I don’t have to create my own mocking classes.
- Action->Assertion > Record->Replay
- Action->Assertion is closer to how developers tend to think about their code
More testable code¶
Why there is resistance to adopting unit testing
Requires deeper knowledge of code
Requires special techniques
You have to invest time to learn it
A lot of managers, especially with large code bases, will not want to make that investment
However, unit testing will improve the design of your code.
- Limit the scope of responsibility
- Create local wrappers
- Deduplicate the code
Practical patterns Part 2: A REST client module¶
- It’s a cient for RabbitMQ’s REST Management API
- ~250 lines of executable code
- ~200 lines of unit test code
- Uses httplib2 to talk to the server
- Tests pass with Python 2.6, 2.7, and 3.2
- Use tox
outstream = StringIO() with patch('sys.stdout', new=outstream) as out: ... actual_out = out.getvalue()
- Mock is going to be in the Python Standard Library in Python 3.3 as unittest.mock.
- Question: How to organize integration tests?
- Question: When do you find doctests useful?
- Question: Design for testability (e.g.: dependency injection) vs. monkey-patching
- Dependency injection is something that you need a team to be bought into
- Comment (from Gary Bernhardt). Decorators make testing harder because they couple things together at compile-time. The solution is dependency injection.