W

Ward
GitHub logo
184 stars, 12 forks

Ward

A testing framework for Python 3.6 and beyond.

Descriptive testing

Describe your tests with strings, not long_and_unreadable_function_names.

def greeting(name):
    return f"Hello, {name}!"

@test("greeting({name}) returns 'Hello, {name}!'")
def _(name="Sam"):
    assert greeting(name) == f"Hello, {name}!"
PASS test_util:7:
greeting('Sam') return 'Hello, Sam!'
FAIL test_users:12:
get_user(id=1) returns user 1
SKIP test_todos:19:
mark_done(todo_id=3) returns todo 3 [WIP]

Readable output

Understand failures quickly with colourful unified diff output.

Comparison: LHS vs RHS shown below

['apples', 'bananas', 'kiwis']
['apples', 'bananas', 'oranges']

Modular test dependencies

Manage test setup and teardown using fixtures that rely on Python's import system, not name matching.

1. Define a unit of test data anywhere, and tell Ward how long to cache it for.
user_fixtures.py
# Will run at most 1x per module
@fixture(scope=Scope.Module)
def user():
    u = User(id=1, name="sam")
    return u
2. Import it and bind it as a default argument. Ward will inject the resolved fixture into your test.
test_users.py
from user_fixtures import user

@test("get_user returns the correct user")
def _(expected=user):
    found = get_user(id=expected.id)
    assert found == expected

Expressive parameterised testing

Parameterise tests to have Ward call your test multiple times with different arguments.

@test("truncate('{text}', num_chars={num_chars}) returns '{expected}'")
def _(
    text=s,
    num_chars=each(20, 11, 10, 5),
    expected=each(s, s, "hello w...", "he..."),
):
    result = truncate(text, num_chars)
    assert result == expected

Ward will expand this into 4 distinct tests, each with their own description:

PASS test_util:47 [1/4]:
truncate('hello world', num_chars=20) returns 'hello world'
FAIL test_util:47 [2/4]:
truncate('hello world', num_chars=11) returns 'hello world'
PASS test_util:47 [3/4]:
truncate('hello world', num_chars=10) returns 'hello w...'
PASS test_util:47 [4/4]:
truncate('hello world', num_chars=5) returns 'he...'

More features

CROSS PLATFORM
Tested on Windows, Mac OS, and Linux systems.
ZERO CONFIG
Sensible defaults make configuration optional.
TEST SEARCH
Loose querying of test code for quick development.
LOW OVERHEAD
Roughly half the framework overhead of Pytest.
WORKS WITH HYPOTHESIS
Works with Hypothesis out of the box, with deeper support planned.

Interested in helping push Ward forward?

Ward is currently in development on GitHub. Contributions of any kind are welcomed!

It's available on PyPI, and can be installed using pip:

$
pip install ward