How to execute tests in parallel using pytest if test file has setup & tear down section

Viewed 1937

My test script is like below

@pytest.fixture(scope="Module", Autouse="True")
def setup_test():
    ....................
    yield
    ............

def test_1()
    ...............

def test_2()
    ...............

def test_3()
 ...............

Sequential script execution is working fine. (First test setup -> Test1 -> Test2 ->Test3 -> Tear down) with pytest.

How to run script execution in parallel like :- First test setup -> All test cases in parallel -> Tear down?

If I use the -n option with pytest execution, it triggers all tests in parallel even before the setup section completes and executes the teardown section after all test cases.

I tried providing the --dist=load option, put setup & tear down in conftest file etc. Nothing worked in my case.

3 Answers

I tried --dist=loadscope, it is executing tests sequentially not in parallel

import logging
import time
import pytest
@pytest.fixture(scope="session", autouse="True")
def create_test_setup():
    logging.info("Setup start")
    time.sleep(10)
    logging.info("Setup ends")
    yield
    logging.info("Tear down start")
    time.sleep(10)
    logging.info("Tear down ends")
def test_1():
    logging.info("Test1 start")
    time.sleep(10)
    logging.info("Test1 ends")
def test_2():
    logging.info("Test2 start")
    time.sleep(20)
    logging.info("Test2 ends")
def test_3():
    logging.info("Test3 start")
    time.sleep(30)
    logging.info("Test3 end")
def test_4():
    logging.info("Test4 start")
    time.sleep(40)
    logging.info("Test4 ends")


pytest -n 4 --dist=loadscope
============================= test session starts ==============================
platform linux -- Python 3.6.8, pytest-5.2.0, py-1.9.0, pluggy-0.13.1 -- 
.......................................
...................................
scheduling tests via LoadScopeScheduling
test.py::test_1
test.py::test_2
test.py::test_3
test.py::test_4
======================== 4 passed in 136.40s (0:02:16) =========================
Process finished with exit code 0
[gw0] [ 25%] PASSED test.py::test_1 2020-07-14 19:10:29,359 - INFO [root] -  test.py:20 - Test1 start
2020-07-14 19:10:39,363 - INFO [root] -  test.py:22 - Test1 ends
[gw0] [ 50%] PASSED test.py::test_2 2020-07-14 19:10:39,377 - INFO [root] -  test.py:26 - Test2 start
2020-07-14 19:10:59,397 - INFO [root] -  test.py:28 - Test2 ends
[gw0] [ 75%] PASSED test.py::test_3 2020-07-14 19:10:59,413 - INFO [root] -  test.py:32 - Test3 start
2020-07-14 19:11:29,436 - INFO [root] -  test.py:34 - Test3 end
[gw0] [100%] PASSED test.py::test_4 2020-07-14 19:11:29,450 - INFO [root] -  test.py:38 - Test4 start
2020-07-14 19:12:09,486 - INFO [root] -  test.py:40 - Test4 ends
2020-07-14 19:12:09,576 - INFO [root] -  test.py:14 - Tear down start
2020-07-14 19:12:19,586 - INFO [root] -  test.py:16 - Tear down ends

PyTest doesn't know that your tests depend on the setup fixture when running in parallel. To tell PyTest which fixtures a test uses, you need to an argument to the test functions with the same name as the fixture - then PyTest will do Magic and know to use the fixture function to create an object to use as that argument. Autouse isn't enough when testing in parallel; this could be considered a bug in parallel mode I suppose, not sure.

@pytest.fixture(scope="Module", Autouse="True")
def setup_test():
    ...............
    yield
    ...............

def test_1(setup_test):
    ...............

def test_2(setup_test):
    ...............

def test_3(setup_test):
    ...............

Note, because each parallel process worker has its own "scope" the setup_test() fixture will be created one in each scope - even though it is marked as Module. Beware if this is managing resources that might collide (e.g. ports, databases, containers, etc). If that is a problem, then they can't be run in parallel (but could be run parallel with other tests that don't use the fixture).

Try --dist=loadscope

import pytest
import logging
logging.basicConfig(format='%(message)s')

@pytest.fixture(scope="session", autouse=True)
def setup_test():
    logging.warning("Setup")
    yield
    logging.warning("Tear down")

def test_1():
    logging.warning("Test1")

def test_2():
    logging.warning("Test2")

def test_3():
    logging.warning("Test3")

Output -

C:\pytest_demo\test3>pytest -n3 -s --dist=loadscope
================================================= test session starts =================================================
platform win32 -- Python 3.7.1, pytest-5.2.2, py-1.7.0, pluggy-0.13.0
rootdir: C:\pytest_demo, inifile: pytest.ini
plugins: allure-pytest-2.8.6, arraydiff-0.3, doctestplus-0.2.0, forked-1.1.3, html-2.0.0, metadata-1.8.0, openfiles-0.3.1, remotedata-0.3.1, xdist-1.30.0
gw0 [3] / gw1 [3] / gw2 [3]
Setup
Test1
.Test2
.Test3
Tear down
.
Related