93

I am using pytest. I have two files in a directory. In one of the files there is a long running test case that generates some output. In the other file there is a test case that reads that output. How can I ensure the proper execution order of the two test cases? Is there any alternative other than puting the test cases in the same file in the proper order?

Kocka
  • 1,634
  • 2
  • 13
  • 21
  • 23
    in general it's not a great idea to have test cases dependent on one another. If you need to use the same data in 2 test cases why not add it to the `setup` section? – Brad Jul 10 '13 at 13:39
  • I know. I don't like it either but right now I need it. – Kocka Jul 10 '13 at 14:00
  • 4
    I don't want to add it to the setup section, because it takes about 15-20 minutes. – Kocka Jul 11 '13 at 05:30
  • 1
    hmm, any way to mock it out? If not then you may need to explicitely link the two tests (or make them one big test) – Brad Jul 11 '13 at 15:55
  • 4
    Shared or environmental setup code goes into the setUpClass method, it will run once, and before any tests run. Alternately you could write lazy-initialization pattern code into the setup method. or even write - `initialized=False def test_mytest1: if initialized: somelongfunction() initialized=True` Rather use the framework. Consider the possibility, that if you need to order tests, they are no longer unit tests, and you need to think about a new level of scaffolding work to simplify this. –  May 09 '18 at 08:08
  • Not ideal but you could enumerate your files based on your order, like so: `1slow_test.py`,`2fast_test.py` – giuliano-oliveira Sep 27 '20 at 14:28
  • 7
    It's worth pointing out here that in the realm of testing things in the real world, eg hardware rigs in the loop, sometimes you need to control the order of things because there's real world state you need to coordinate which may not have a equivalence in pure software tests. – jxramos Oct 23 '20 at 20:39
  • Here's another answer showing how to do this for slow tests, but can easily be used for any kind of marker. https://stackoverflow.com/questions/61533694/run-slow-pytest-commands-at-the-end-of-the-test-suite – asmeurer Apr 21 '21 at 21:13

9 Answers9

94

In general you can configure the behavior of basically any part of pytest using its well-specified hooks.

In your case, you want the "pytest_collection_modifyitems" hook, which lets you re-order collected tests in place.

That said, it does seem like ordering your tests should be easier -- this is Python after all! So I wrote a plugin for ordering tests: "pytest-ordering". Check out the docs or install it from pypi. Right now I recommend using @pytest.mark.first and @pytest.mark.second, or one of the @pytest.mark.order# markers, but I have some ideas about more useful APIs. Suggestions welcome :)

Edit: pytest-ordering seems abandoned at the moment, you can also check out pytest-order (a fork of the original project by the author).

Edit2: In pytest-order, only one marker (order) is supported, and the mentioned examples would read @pytest.mark.order("first"), @pytest.mark.order("second"), or @pytest.mark.order(#) (with # being any number).

MrBean Bremen
  • 14,916
  • 3
  • 26
  • 46
Frank T
  • 8,268
  • 8
  • 50
  • 67
  • Hey. Thanks for creating this plugin. Can you modify it such that it just extracts trailing digits as the order. E.g If I say @pytest.mark.custom1 , then 1 should be the order. This would greatly help! – semantic_c0d3r Jul 25 '14 at 22:43
  • I can try. I think v0.2 has this behavior with an "order" marker, instead of arbitrary markers. Could you write this up as a feature request on pytest-ordering's github page? https://github.com/ftobia/pytest-ordering – Frank T Jul 27 '14 at 14:36
  • The hooks link should go to: http://pytest.org/latest/writing_plugins.html#well-specified-hooks – Nick Chammas Nov 30 '15 at 23:35
  • I've noticed in pytest v3.0.3 that the order of tests in the `items` list in `pytest_collection_modifyitems()` doesn't match the order that the tests are actually executed, and I'm not sure why. – davidA Dec 08 '16 at 23:11
  • 2
    @meowsqueak have you deduced why the order changes? – msudder Jul 17 '17 at 20:36
  • @msudder I didn't, unfortunately. I ended up making sure that my tests could run in any order (probably a sensible thing anyway) and that worked around the issue. – davidA Jul 22 '17 at 05:25
  • LInk to well-specified hooks is dead @FrankT – Nam G VU Dec 16 '19 at 05:10
  • 2
    The pytest-ordering plugin appears to be abandoned at the moment. An actively-maintained fork of it has been created named [pytest-order](https://github.com/mrbean-bremen/pytest-order) and should probably be used instead. @FrankT Can you modify your answer to indicate pytest-order should be used? Individuals who Google for "pytest order tests" (or similar searches) might be running into this post and be misled by the answer in its current state. – Christopher Hart Jan 08 '21 at 00:32
  • Should we write `@pytest.mark.order("number")` on top of each function for this to work? – alper Feb 26 '23 at 21:24
27

Maybe you can consider using dependency pytest plugin, where you can set the test dependencies easily.

Be careful - the comments suggest this does not work for everyone.

@pytest.mark.dependency()
def test_long():
    pass

@pytest.mark.dependency(depends=['test_long'])
def test_short():
    pass

This way test_short will only execute if test_long is success and force the execution sequence as well.

Roelant
  • 4,508
  • 1
  • 32
  • 62
asterio gonzalez
  • 1,056
  • 12
  • 12
  • Above does not work for me for order sequencing. For eg: if you flip the order. Ie Have long depend on short. In that case, long is skipped. – peaxol Nov 09 '17 at 22:34
  • 1
    This answer uses the pytest plugin [pytest-dependency](https://pypi.org/project/pytest-dependency/) – Frank Robert Anderson Jun 14 '18 at 14:22
  • 1
    The tests sit in two different modules and as pointed out in https://github.com/RKrahl/pytest-dependency/issues/3 it still is not possible to have a test depends to another test in another module – Stefano Messina Jul 06 '18 at 13:11
  • 1
    This is not working with pytest-dependency 0.5.1 (I just tried). pytest-depedency will simply skip a test if it wasn't reached yet, it will not order tests. – Tal Jan 13 '21 at 19:08
  • I'm a about to down-vote this answer, as this is either a bug in dependency-0.5.1 or the ordering feature is no longer in the scope of pytest-dependency. See also https://github.com/RKrahl/pytest-dependency/issues/20 – Axel Heider Jan 24 '21 at 19:09
  • As long as the issue is not fixed in pytest-dependency, you can use [pytest-order](https://github.com/pytest-dev/pytest-order) with the option `--order-dpendencies` - this will re-order the tests accordingly (_Disclaimer_: I'm the owner of that fork, and it may not work in all cases). – MrBean Bremen Mar 04 '21 at 19:41
  • I have the same issue, the dependencies don't work at all :( – Roelant Mar 23 '21 at 15:25
12

Pytest's fixtures can be used to order tests in a similar way they order the creation of fixtures. While this is unconventional, it takes advantage of knowledge you may already have of the fixture system, it does not require a separate package and is unlikely to be altered by pytest plugins.

@pytest.fixture(scope='session')
def test_A():
    pass

@pytest.mark.usefixtures('test_A')
def test_B():
    pass

The scope prevents multiple calls to test_A if there are multiple tests that depend on it.

Brian C.
  • 6,455
  • 3
  • 32
  • 42
Ali Zwd
  • 145
  • 2
  • 4
  • 14
    While this code may answer the question, it is better to explain how to solve the problem and provide the code as an example or reference. Code-only answers can be confusing and lack context. – Robert Columbia May 29 '18 at 03:18
  • I got this when I use function scope fixtures `ScopeMismatch: You tried to access the function scoped fixture xxx with a session scoped request object, involved factories`. I will try to use this to fix it: [Py.test fixture: Use function fixture in scope fixture](https://stackoverflow.com/questions/45817153/py-test-fixture-use-function-fixture-in-scope-fixture). – Evandro Coan Jul 31 '22 at 18:25
11

As indicated by @Frank T in the accepted answer, the pytest_collection_modifyitems hook hook allows to modify the order of collected tests (items) in place. This approach has the advantage of not requiring any third party library.

A full example on how to enforce test case execution order by test class is already available in this answer.

In this case, though, it seems like you would like to enforce execution order by test module (i.e., the .py file where the test lives). The following adaptation would allow you to do so:

# conftest.py
def pytest_collection_modifyitems(items):
    """Modifies test items in place to ensure test modules run in a given order."""
    MODULE_ORDER = ["tests.test_b", "tests.test_c", "tests.test_a"]
    module_mapping = {item: item.module.__name__ for item in items}

    sorted_items = items.copy()
    # Iteratively move tests of each module to the end of the test queue
    for module in MODULE_ORDER:
        sorted_items = [it for it in sorted_items if module_mapping[it] != module] + [
            it for it in sorted_items if module_mapping[it] == module
        ]
    items[:] = sorted_items

Placing the snippet above in conftest.py replaces the default alphabetical test execution order test_a -> test_b -> test_c with test_b -> test_c -> test_a. Modules can live in different test sub-directories, and the order of tests inside a module is left unchanged.

swimmer
  • 1,971
  • 2
  • 17
  • 28
7

It's important to keep in mind, while trying to fix pytest ordering "issue", that running tests in the same order as they are specified seems to be the default behavior of pytest.

It turns out that my tests were out of that order because of one of these packages - pytest-dependency, pytest-depends, pytest-order. Once I uninstalled them all with pip uninstall package_name, the problem was gone. Looks like they have side effects

Sergey Pleshakov
  • 7,964
  • 2
  • 17
  • 40
2

To me the simpliest way to fix tests execution order is to use them as fixtures, which are executed ordered by design.

@pytest.fixture()
def test_a():
    print("running test a first")
    pass

def test_b(test_a):
    print("running test b after test a")
    pass

Mark test dependency as fixture and pass it as an argument to dependent one.

Pangur
  • 586
  • 4
  • 10
1

main.py:

import functools
import pytest
from demo import test_foo,test_hi

def check_depends(depends):
    try:
        for dep in depends:
            dep()
    except Exception as e:
        return dep
    else:
        return True

def pytest_depend(depends):
    def pytest_depend_decorator(func):
        stat = check_depends(depends)
        if stat is True:
            return func
        else:
            return pytest.mark.skip(True, reason="%s[skip] --> %s[Failed]" % (func.__name__, stat.__name__))(func)
    return pytest_depend_decorator


@pytest_depend([test_foo,test_hi])
def test_bar():
    pass

@pytest_depend([test_foo,test_hi])
def test_bar2():
    pass

demo.py:

def test_hi():
    pass
def test_foo():
    assert False

platform linux -- Python 3.5.2, pytest-3.8.2, py-1.6.0, pluggy-0.7.1 -- /usr/bin/python3

pytest -vrsx ./plugin.py

zhihao he
  • 11
  • 1
  • I'm not sure I would recommend using this. It will run test_foo and test_hi for each test_bar, instead of running it once. Also, if test_foo or test_hi is called during the test (as its own test) then also you wouldn't know. – Eran Yogev Feb 01 '21 at 07:55
-1

Make use of the '--randomly-dont-reorganize' option or '-p no:randomly' available in pytest-randomly plugin, this will just run your test in the same order as you mentioned in your module.

Module:

import pytest

def test_three():
    assert True

def test_four():
    assert True

def test_two():
    assert True

def test_one():
    assert True

Execution:

(tmp.w95BqE188N) rkalaiselvan@dev-rkalaiselvan:~/$ py.test --randomly-dont-reorganize test_dumm.py
======================================================================== test session starts ========================================================================
platform linux2 -- Python 2.7.12, pytest-3.10.1, py-1.5.4, pluggy-0.7.1 -- /tmp/tmp.w95BqE188N/bin/python2
cachedir: .pytest_cache
Using --randomly-seed=1566829391
rootdir: /home/rkalaiselvan, inifile: pytest.ini
plugins: randomly-1.2.3, timeout-1.3.1, cov-2.6.0, mock-1.10.0, ordering-0.6
collected 4 items

test_dumm.py::test_three PASSED
test_dumm.py::test_four PASSED
test_dumm.py::test_two PASSED
test_dumm.py::test_one PASSED

(tmp.w95BqE188N) rkalaiselvan@dev-rkalaiselvan:~/$ py.test -p no:randomly test_dumm.py
======================================================================== test session starts ========================================================================
platform linux2 -- Python 2.7.12, pytest-3.10.1, py-1.5.4, pluggy-0.7.1 -- /tmp/tmp.w95BqE188N/bin/python2
cachedir: .pytest_cache
Using --randomly-seed=1566829391
rootdir: /home/rkalaiselvan, inifile: pytest.ini
plugins: randomly-1.2.3, timeout-1.3.1, cov-2.6.0, mock-1.10.0, ordering-0.6
collected 4 items

test_dumm.py::test_three PASSED
test_dumm.py::test_four PASSED
test_dumm.py::test_two PASSED
test_dumm.py::test_one PASSED
Ravichandran K
  • 378
  • 3
  • 11
-10

Make Sure you have installed pytest-ordering package. To confirm go to Pycharm settings>>Project Interpreter >> and look for pytest-ordering : If it is not available install it. Pycharm settings>>Project Interpreter >> Click on + button and search pytest-ordering install it. Voila!! It will definitely work.

Amol Kumar
  • 121
  • 1
  • 4