Skip to main content
Join the official 2020 Python Developers SurveyStart the survey!

A Python 3 test framework for finding flaws faster.

Project description


A modern Python test framework designed to help you find and fix flaws faster.



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

  • Colourful, human readable output: quickly pinpoint and fix issues with detailed output for failing tests.
  • Modular test dependencies: manage test setup/teardown code using modular pytest-style fixtures.
  • Expect API: A simple but powerful assertion API inspired by Jest.
  • Cross platform: Tested on Mac OS, Linux, and Windows.
  • Zero config: Sensible defaults mean running ward with no arguments is enough to get started.

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

Getting Started

Install Ward with pip install ward.

Write your first test in (module name must start with "test"):

from ward import expect

def test_one_plus_two_equals_three():  # name must start with "test"
    expect(1 + 2).equals(3)

Now run your test with ward (no arguments needed). Ward will output the following:

 PASS  test_sum.test_one_plus_two_equals_three

You've just wrote your first test with Ward, congrats! Look here for more examples of how to test things with Ward.

How to Contribute

Contributions are very welcome and encouraged!

See the contributing guide for information on how you can take part in the development of Ward.

More 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

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:

def database():
    db_conn = setup_database()
    yield db_conn

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)

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

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

def test_capital_cities(cities):
    found_cities = get_capitals_from_server()

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

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):

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

def test_to_be_skipped():

Expecting a test to fail

You can mark a test that you expect to fail with the @xfail decorator. If a test marked with this decorator passes unexpectedly, the overall run will be considered a failure.

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.

Files for ward, version 0.9.1a0
Filename, size File type Python version Upload date Hashes
Filename, size ward-0.9.1a0-py3-none-any.whl (15.3 kB) File type Wheel Python version py3 Upload date Hashes View
Filename, size ward-0.9.1a0.tar.gz (12.0 kB) File type Source Python version None Upload date Hashes View

Supported by

Pingdom Pingdom Monitoring Google Google Object Storage and Download Analytics Sentry Sentry Error logging AWS AWS Cloud computing DataDog DataDog Monitoring Fastly Fastly CDN DigiCert DigiCert EV certificate StatusPage StatusPage Status page