Skip to main content

A Python 3 test framework.

Project description

Ward

An experimental test runner for Python 3.6+ that is heavily inspired by pytest. This project is a work in progress, and is not production ready.

screenshot

Features

This project is a work in progress. Some of the features that are currently available in a basic form are listed below.

  • Modular setup/teardown with fixtures and dependency injection
  • Highly readable, colourful diffs intended to be as readable as possible
  • A human readable assertion API
  • Tested on Mac OS, Linux, and Windows
  • stderr/stdout captured during test and fixture execution

Planned features:

  • Smart test execution order designed to surface failures faster (using various heuristics)
  • Multi-process mode to improve performance
  • Highly configurable output modes
  • Code coverage with --coverage flag
  • Handling flaky tests with test-specific retries, timeouts
  • Integration with unittest.mock (specifics to be ironed out)
  • Plugin system
  • Highlighting diffs on a per-character basis, similar to diff-so-fancy (right now it's just per line)

Quick Start

Installation: pip install ward

Look for tests recursively and run them: ward

Examples

Dependency injection with fixtures

In the example below, we define a single fixture named cities. Our test takes a single parameter, which is also named cities. Ward sees that the fixture name and parameter names match, so it calls the cities fixture, and passes the result into the test.

from ward import expect, fixture

@fixture
def cities():
    return ["Glasgow", "Edinburgh"]

def test_using_cities(cities):
    expect(cities).equals(["Glasgow", "Edinburgh"])

Fixtures are great for extracting common setup code that you'd otherwise need to repeat at the top of your tests, but they can also execute teardown code:

@fixture
def database():
    db_conn = setup_database()
    yield db_conn
    db_conn.close()


def test_database_connection(database):
    # The database connection can be used in this test,
    # and will be closed after the test has completed.
    users = get_all_users(database)
    expect(users).contains("Bob")

The Expect API

In the (contrived) test_capital_cities test, we want to determine whether the get_capitals_from_server function is behaving as expected, so we grab the output of the function and pass it to expect. From here, we check that the response is as we expect it to be by chaining methods. If any of the checks fail, the expect chain short-circuits, and the remaining checks won't be executed for that test. Methods in the Expect API are named such that they correspond as closely to standard Python operators as possible, meaning there's not much to memorise.

from ward import expect, fixture

@fixture
def cities():
    return {"edinburgh": "scotland", "tokyo": "japan", "madrid": "spain"}

def test_capital_cities(cities):
    found_cities = get_capitals_from_server()

    (expect(found_cities)
     .contains("tokyo")                                 # it contains the key 'tokyo'
     .satisfies(lambda x: all(len(k) < 10 for k in x))  # all keys < 10 chars
     .equals(cities))

Checking for exceptions

The test below will pass, because a ZeroDivisionError is raised. If a ZeroDivisionError wasn't raised, the test would fail.

from ward import raises

def test_expecting_an_exception():
    with raises(ZeroDivisionError):
        1/0

Running tests in a directory

You can run tests in a specific directory using the --path option. For example, to run all tests inside a directory called tests:

ward --path tests

To run tests in the current directory, you can just type ward, which is functionally equivalent to ward --path .

Filtering tests by name

You can choose to limit which tests are collected and ran by Ward using the --filter option. Test names which contain the argument value as a substring will be run, and everything else will be ignored.

To run a test called test_the_sky_is_blue:

ward --filter test_the_sky_is_blue

The match takes place on the fully qualified name, so you can run a single module (e.g. my_module) using the following command:

ward --filter my_module.

Skipping a test

Use the @skip annotation to tell Ward not to execute a test.

from ward import skip

@skip
def test_to_be_skipped():
    pass

Testing for approximate equality

Check that a value is close to another value.

expect(1.0).approx(1.01, epsilon=0.2)  # pass
expect(1.0).approx(1.01, epsilon=0.001)  # fail

Cancelling a run after a specific number of failures

If you wish for Ward to cancel a run immediately after a specific number of failing tests, you can use the --fail-limit option. To have a run end immediately after 5 tests fail:

ward --fail-limit=5

Project details


Release history Release notifications | RSS feed

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ward-0.8.0a0.tar.gz (11.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ward-0.8.0a0-py3-none-any.whl (14.4 kB view details)

Uploaded Python 3

File details

Details for the file ward-0.8.0a0.tar.gz.

File metadata

  • Download URL: ward-0.8.0a0.tar.gz
  • Upload date:
  • Size: 11.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/2.0.0 pkginfo/1.5.0.1 requests/2.22.0 setuptools/41.4.0 requests-toolbelt/0.9.1 tqdm/4.36.1 CPython/3.7.4

File hashes

Hashes for ward-0.8.0a0.tar.gz
Algorithm Hash digest
SHA256 e235d34907632f5b6a33dd9170cab885b8b329cb5130003a22e5292846001399
MD5 a17d3cf1c35c9f136b614517711e10c1
BLAKE2b-256 5005bdc1bb0fd8d22b50ce82848ab8f5a310cf5181d8c6ddfb29fccd5f3067f7

See more details on using hashes here.

File details

Details for the file ward-0.8.0a0-py3-none-any.whl.

File metadata

  • Download URL: ward-0.8.0a0-py3-none-any.whl
  • Upload date:
  • Size: 14.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/2.0.0 pkginfo/1.5.0.1 requests/2.22.0 setuptools/41.4.0 requests-toolbelt/0.9.1 tqdm/4.36.1 CPython/3.7.4

File hashes

Hashes for ward-0.8.0a0-py3-none-any.whl
Algorithm Hash digest
SHA256 4bfbc658b83dcaec2757d879897d1d95908b5bf1fde988a5ee7c1c7ac762cef5
MD5 4d6921396f4c8ff8b965dd22a62b2b75
BLAKE2b-256 b7e9fa69480df9fc761a42d879e4bbab77669508f6c4ca5aa46232375f59a7cb

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page