Date Category blog

Catego Tags: python, testing, pytest Status: draft

About pytest

From pytest.org:

  • a mature full-featured Python testing tool
  • provides easy no-boilerplate testing
  • scales from simple unit to complex functional testing
  • integrated with other testing methods and tools
  • extensive plugin and customization system

Installing pytest

If you have already created your virtualenv, download the latest pytest package and run pip install pytest-[version].tar.gz.

Writing your tests

Basic example

This is the most basic example, adapted from the Python Guide:

# content of test_sample.py
def func(x):
    return x + 1

def test_func():
    assert func(3) == 5

You only need to prefix your test function with test_ so that Pytest can find it. A good practice is to use the name of the function you are testing after the prefix.

Mocking modules and environments

From Pytest:

Sometimes tests need to invoke functionality which depends on global settings or which invokes code which cannot be easily tested such as network access. The monkeypatch function argument helps you to safely set/delete an attribute, dictionary item or environment variable or to modify sys.path for importing.

Monkeypatch is a module that helps you set the return values for other functions that your function depends on.

If you want to pretend that os.expanduser returns a certain directory, you can use the monkeypatch.setattr() method to patch this function before calling into a function which uses it:

# content of test_module.py
import os.path
def getssh(): # pseudo application code
    return os.path.join(os.path.expanduser("~admin"), '.ssh')

def test_mytest(monkeypatch):
    def mockreturn(path):
        return '/abc'
    monkeypatch.setattr(os.path, 'expanduser', mockreturn)
    x = getssh()
    assert x == '/abc/.ssh'

Mock

Another useful module is Mock, which now comes with Unittest for Python 3.3 onward.

Let's mock a class, for example:

my_mock = Mock()
my_mock.some_attribute = 5
my_mock.some_method = Mock()
my_mock.some_method.return_value = 10

Now, my_mock.some_method() will return 10. Similarly, my_mock.some_attribute will return 3.

Running your tests

To run your tests, you only need run the command from your tests directory.

py.test -v

Note: Remember to install the package you want to test beforehand with python setup.py install.

Test discovery

Pytest has a 'discovery' feature to automatically find your tests within your code. This is how it works by default:

  • collection starts from the initial command line arguments which may be directories, filenames or test ids.
  • recurse into directories, unless they match norecursedirs
  • test_*.py or *_test.py files, imported by their package name.
  • Test prefixed test classes (without an __init__ method)
  • test_ prefixed test functions or methods are test items

Note: If you run them from your package directory, and you have your virtualenv directory in there, then pytest will find the tests for the packages installed and run them.

Verifying test coverage

Pytest has a plugin called pytest-cov for coverage reporting of your tests. You can install this package using pip.

For verifying centralized testing coverage in EC, we would run the following:

py.test --cov package-path/ tests-path/

Note: Pytest-cov depends on cov-core and on coverage.

Further reading