Here are some of the key differences between ward and pytest. This article assumes a working knowledge of pytest concepts.
In pytest, you inject a fixture into a test by defining a parameter of the same name. To share pytest fixtures across files, you can place them in a
@pytest.fixturedef num():return 42def test_number_is_42(num): # param name must match fixture nameassert num == 42
In ward, you inject a fixture by importing it and binding it as a default argument in your test. This means IDE features such as "go to definition" work for fixtures. You don't need to learn rules for fixture resolution or worry that you may override a fixture or inject the wrong one.
@fixturedef num():yield 42@test("the number is 42")def _(n=num):assert n == 42
Alternatively, you can use the
@using decorator to bind a fixture to a positional argument:
@test("the number is 42")@using(n=num)def _(n):assert n == 42
To share ward fixtures across files, you can just import them as you would any other Python function. In both ward and pytest, you can execute teardown code in a fixture by placing it after a yield statement.
Pytest test names must begin with the word "test". They can be functions or methods in a class.
In ward, tests are indicated using the
@test(description) decorator. The function itself can be named
_, since the name would likely just repeat the description in a less readable form. Class level tests are not supported.
In both cases, tests are collected from modules starting with
"test_" or ending with
Pytest currently supports
session scopes. As of v5.2, it now supports dynamic scopes.
Ward currently supports
Test (which I think is equivalent to pytest
Global (same as pytest
Both Ward and Pytest support assertion rewriting, meaning you
can use the Python
assert statement and still get detailed information on the assertion should it fail.
Pytest rewrites assertions using an import hook, whereas Ward rewrites the bodies of tests that are collected.
In pytest, you can parameterise tests using the
@pytest.mark.parametrize("test_input,expected", [("3+5", 8), ("2+4", 6), ("6*9", 42)])def test_eval(test_input, expected):assert eval(test_input) == expected
This test translated into ward would look like:
@test("eval returns expected output")def _(test_input=each("3+5", "2+4", "6*9"),expected=each(8, 6, 42),):assert eval(test_input) == expected
There appears to be no simple way to use a fixture when parameterising a test in pytest. In ward, if you refer to a fixture when parameterising, it will be resolved:
@fixturedef forty_two():yield 42@test("eval returns expected output")def _(test_input=each("3+5", "2+4", "6*9"),expected=each(8, 6, forty_two),):assert eval(test_input) == expected
Pytest also lets you parameterise fixtures themselves. Ward does not support this.
A comparison between the output of Pytest and Ward is shown below.
Pytest was run with the
-v flag in order to display the full diffs
for failing tests. Unfortunately, when you run pytest with
of the output becomes more verbose. If you want to see a full diff for
any tests that fail, you have to accept that pytest will print each test
on a separate line during the run:
Ward relies on colours rather than symbols to present diffs:
Pytest is a mature production ready testing framework. On the other hand, Ward has not been production tested and is currently in beta.
Pytest has a plugin system powered by
pluggy. Ward does not yet have a