Testing with pytest
Danilo Bargen
Webrepublic AG
2014-01-10
Why pytest?
unittest
import unittest
class TestTheAnswer(unittest.TestCase):
def testIt(self):
self.assertEquals(19 + 23, 42)
pytest
def test_the_answer():
assert 19 + 23 == 42
Features
- Simple assertions
- Dependency injection
- Parametrized tests
- Test discovery
- Test runner
- Distributed testing
- Plugin support (e.g. PEP8,
coverage or pytest-django) - Conditonal skipping
- Easy to get started
- ...
Features
/
Examples
Test Discovery
- Good practice: Place tests in a tests/ directory
- Naming
- Module naming: test_*.py or *_test.py
- Function naming: test_* or *_test
- Class naming: Test* or *Test
- Customizable!
- Run py.test command
Simple Tests
Just use "assert"
def test_a_feature():
spam = do_stuff()
assert spam > 9000, "Spam is not over nine thousand"
Exceptions
import pytest
def raise_exception():
raise RuntimeError('OMGWTF')
def test_exception_is_raised():
with pytest.raises(RuntimeError):
raise_exception()
Explicit Fails
import pytest
def test_eggs_import():
try:
import eggs
except ImportError:
pytest.fail('Could not import eggs.')
Grouping Tests
class TestClass(object):
def test_one(self):
x = 'this'
assert 'h' in x
def test_two(self):
x = 'hello'
assert hasattr(x, 'check')
Setup / Teardown (1/2)
Module Level
def setup_module(module):
def teardown_module(module):
Class Level
@classmethod
def setup_class(cls):
@classmethod
def teardown_class(cls):
Setup / Teardown (2/2)
Method Level
def setup_method(self, method):
def teardown_method(self, method):
Function Level
def setup_function(function):
def teardown_function(function):
Fixtures
Many preprovided fixtures.
Dependency injection.
def test_write_tempfile(tmpdir):
p = tmpdir.mkdir('sub').join('hello.txt')
p.write('content')
assert p.read() == "content"
assert len(tmpdir.listdir()) == 1
Custom Fixtures
import pytest
@pytest.fixture
def smtp():
import smtplib
return smtplib.SMTP('mail.webrepublic.ch')
def test_ehlo(smtp):
response, msg = smtp.ehlo()
assert response == 250
assert 'webrepublic' in msg
Parametrized Tests
A test will be generated for each argument.
import pytest
@pytest.mark.parametrize(['a', 'b'], [
(19, 23),
(13, 29),
(42, 0),
(65, -23),
])
def test_answer_sum(a, b):
assert a + b == 42
Drop to Debugger
It's possible to drop into PDB or IPDB
when a test fails.
$ py.test [--pdb|--ipdb]
Case Study:
Campaign Radar
requirements.txt
pytest
pytest-django
pytest-pep8
coverage
pytest.ini
pytest]
DJANGO_SETTINGS_MODULE = config.settings
addopts = --pep8 --tb=short
python_files = test_*.py
pep8ignore =
*.py E124 E126 E127 E128
setup.py ALL
settings.py ALL
*/migrations/* ALL
*/tests/* ALL
pep8maxlinelength = 99
.coveragerc (1/2)
[run]
source =
front
worker
omit =
*/migrations/*
*/tests/*
.coveragerc (2/2)
[report]
# Regexes for lines to exclude from consideration
exclude_lines =
# Don't complain about missing debug-only code:
def __repr__
if self\.debug
# Don't complain if tests don't hit defensive assertion code:
raise AssertionError
raise NotImplementedError
# Don't complain if non-runnable code isn't run:
if 0:
if __name__ == .__main__.:
Testing
Install current module:
pip install -e .
Run all tests:
py.test
Measuring coverage:
coverage run -m py.test
coverage report
Testing Output
================================================================
test session starts
=================================================================
platform linux2 -- Python 2.7.6 -- pytest-2.4.2
plugins: pep8, cov, cache, django
collected 63 items
manage.py s
tests/front/test_testing.py .
tests/worker/test_error_detection.py ................................
tests/worker/test_tasks.py .
...
=======================================================
34 passed, 29 skipped in 5.24 seconds
========================================================
Coverage Report
Name Stmts Miss Cover
-------------------------------------------------------------------------
front/__init__ 0 0 100%
front/ajax 64 64 0%
front/crispy_layouts 4 4 0%
front/forms 28 28 0%
front/mixins 6 6 0%
worker/error_detection 51 6 88%
worker/management/commands/recurring_checks 39 39 0%
worker/models 1 0 100%
worker/redis 14 10 29%
worker/tasks 246 201 18%
worker/views 0 0 100%
...
-------------------------------------------------------------------------
TOTAL 947 770 19%
Questions?
Future Topics:
- Tox
- Jenkins
- Mocking
Testing with PyTest
By Danilo Bargen
Testing with PyTest
- 2,207