繁体   English   中英

pytest 固定装置和线程同步

[英]pytest fixtures and threads synchronizations

我正在尝试使用 pytest-xdist 以使我的测试并行运行,问题是每个线程都将转到与所有测试共享的夹具并根据线程数执行它。

它给我带来了一个问题,因为该夹具的作用是为我的测试创建数据,一旦它已经创建,我就会得到和错误,因为它已经创建(通过 REST)。

conftest.py:

lock = threading.Lock()

@pytest.fixture(scope=session)
def init_data(request):

    lock.acquire()
    try:
        data = []
        if checking_data_not_created():
            data.append(some_rest_command_creating_data_1())
            data.append(some_rest_command_creating_data_2())
    finally:
        lock.release()

    yield data

    lock.acquire()
    try:
        remove_all_data()
    finally:
        lock.release()

测试类.py:

class classTests():

    def first_test(init_data):
        test body and validation....

     def second_test(init_data):
        test body and validation....

我正在使用命令:pytest -v -n2

假设第一个线程应该运行 first_test() 并且第二个线程应该运行 second_test() 其中一个总是会失败,因为第一个已经在fixtures部分创建了数据,另一个线程将出现异常并且他应该运行的所有测试都将失败。

如您所见,我尝试使用锁来同步线程,但它也不起作用。

知道如何克服这个问题吗?

谢谢。

这种方法不适用于 pytest-xdist,因为它使用多处理而不是多线程,但是它可以使用 --tests-per-worker 选项与pytest-parallel 一起使用,它将使用多个线程运行测试。

使用以下夹具在多线程 pytest 执行中仅设置一次并清理一次数据:

conftest.py:

lock = threading.Lock()
threaded_count = 0

@pytest.fixture(scope='session')
def init_data():
    global lock
    global threaded_count

    lock.acquire()
    threaded_count += 1
    try:
        if threaded_count == 1:
            # Setup Data Once
            data = setup_data()
    finally:
        lock.release()

    yield data

    lock.acquire()
    threaded_count -= 1
    try:
        if threaded_count == 0:
            # Cleanup Data Once
            data = cleaup_data()
    finally:
        lock.release()

命令:

pytest -v --tests-per-worker 2

请注意, pytest-xdist 不支持'session'范围的装置。 pytest-xdist 中的'session'范围夹具实际上意味着特定于进程的会话级夹具,因此它为每个进程单独创建和拆除,并且夹具的状态不在进程之间共享。 有一些长期存在的建议将真正的会话范围固定装置的锁定和共享添加到 pytest-xdist,但它们都遇到了许多经典原因,不惜一切代价尝试避免多线程或多处理与锁定和同步,因此它被 pytest-xdist 开发人员严重贬低(可以理解)。

Python xdist 有办法做到这一点

我相信你的例子,应用他们的建议看起来像这样:

def create_data():
    data = []
    data.append(some_rest_command_creating_data_1())
    data.append(some_rest_command_creating_data_2())

    return data


@pytest.fixture(scope="session")
def session_data(request, tmp_path_factory, worker_id):
    if worker_id == "master":
        # Not running multiple processes, just create the data.
        data = create_data()
    else:
        # Running multiple processes, manage lockfile.
        root_tmp_dir = tmp_path_factory.getbasetemp().parent

        fn = root_tmp_dir / "data.json"
        with FileLock(str(fn) + ".lock"):
            if fn.is_file():
                # Data has been created, read it in.
                data = json.loads(fn.read_text())
            else:
                # Data not created, create and write it out.
                data = create_data()
                fn.write_text(json.dumps(data))

    yield data

    remove_all_data()

与您的示例不同,此示例通过保存在tmp_path_factory.getbasetemp().parent共享临时测试目录中的锁文件提供data ,该文件随 pytest 一起提供

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM