pytest fixture 总是返回一个函数

12 投票
2 回答
11599 浏览
提问于 2025-04-18 18:55

我想从一个固定装置(fixture)中返回一个值,这个值可以被多个测试或测试类使用,但传递的值是一个函数。

这是我的代码:

import pytest

@pytest.fixture()
def user_setup():
    user = {
        'name': 'chad',
        'id': 1
    }
    return user

@pytest.mark.usefixtures('user_setup')
class TestThings:
    def test_user(self):
        assert user_setup['name'] == 'chad'

输出结果是:

=================================== FAILURES ===================================
_____________________________ TestThings.test_user _____________________________

self = <tests.test_again.TestThings instance at 0x10aed6998>

    def test_user(self):
>       assert user_setup['name'] == 'chad'
E       TypeError: 'function' object has no attribute '__getitem__'

tests/test_again.py:14: TypeError
=========================== 1 failed in 0.02 seconds ===========================

但是如果我把测试重写,不使用 usefixtures 装饰器,它就能正常工作:

def test_user(user_setup):
    assert user_setup['name'] == 'chad'

有没有人知道为什么在我尝试使用装饰器的方法时,它不工作呢?

2 个回答

-3

在这两种情况下,全局范围内的 user_setup 都是指向一个函数。区别在于,在你没有使用fixture的版本中,你创建了一个同名的参数,这样很容易让人混淆。

在那个没有fixture的版本中,在 test_user 的范围内,你的 user_setup 指代的是你传入的内容,而不是全局范围中的函数。

我想你可能是想调用 user_setup 并对结果进行下标操作,就像这样:

assert user_setup()['name'] == 'chad'
20

当你使用 @pytest.mark.usefixtures 这个标记时,如果你想把某个“夹具”(fixture)注入到你的测试函数里,你仍然需要提供一个同名的输入参数。

根据 py.test 文档中关于夹具的说明

夹具函数的名字可以在运行测试之前被引用来调用它…… 测试函数可以直接使用夹具的名字作为输入参数,这样夹具函数返回的实例就会被注入。

所以,仅仅使用 @pytest.mark.usefixtures 装饰器只会调用这个函数。如果你提供了一个输入参数,就会得到这个函数的返回结果。

你只有在想调用一个夹具,但又不想把它作为测试的输入参数时,才需要使用 @pytest.mark.usefixtures。这在 py.test 文档中有说明

你收到的关于 user_setup 是一个函数的异常,是因为在你的 test_user 函数内部,user_setup 实际上指的是你在文件中之前定义的那个函数。为了让你的代码按预期工作,你需要在 test_user 函数中添加一个参数:

@pytest.mark.usefixtures('user_setup')
class TestThings:
    def test_user(self, user_setup):
        assert user_setup['name'] == 'chad'

现在从 test_user 函数的角度来看,user_setup 这个名字会指向这个函数的参数,而这个参数就是由 py.test 注入的夹具返回的值。

但其实你根本不需要使用 @pytest.mark.usefixtures 装饰器。

撰写回答