python - pytest ScopeMismatch 错误 : how to use fixtures properly

标签 python pytest pytest-django

对于下面的一段代码:

@pytest.fixture(scope="module")
def dummy_article(request, db):
    return mixer.blend("core.article", title="this one price", internal_id=3000)


def test_article_str_method(dummy_article):
    assert (
        str(dummy_article)
        == f"article with ID {dummy_article.internal_id} and title: {dummy_article.title}"
    )

我收到以下错误:

ScopeMismatch: You tried to access the 'function' scoped fixture 'db' with a 'module' scoped request object, involved factories
core/tests/test_article_model.py:13:  def dummy_article(request, db)

如果我将 fixture 更改为使用 scope="function",错误就会消失,但这违背了将其用于其他测试而不必为每个测试“设置”的目的测试。

我怎样才能拥有具有比 function 更大范围的 db 访问权限的 fixture?

最佳答案

db fixture 具有 function 作用域是有原因的,因此每次测试结束时的事务回滚确保数据库保持与测试时相同的状态测试开始。尽管如此,您可以使用 django_db_blocker fixture 对 fixture 中的数据库进行 session /模块范围的访问:

@pytest.fixture(scope='module')
def get_all_models(django_db_blocker):
    with django_db_blocker.unblock():
        return MyModel.objects.all()

警告

请注意,在 session 范围内解锁数据库时,如果您在其他固定装置或测试中更改了数据库,则您只能靠自己了。在下面的示例中,我在 session 范围的 fixture create_foo 中创建了一个 Foo 实体,然后在 all_foos 中缓存 session 的查询集:

# models.py

from django.db import models

class Foo(models.Model):
    name = models.CharField(max_length=16)


# test_foo.py

import pytest
from app.models import Foo

@pytest.fixture(scope='session', autouse=True)
def create_foo(django_db_blocker):
    with django_db_blocker.unblock():
        Foo.objects.create(name='bar')


@pytest.fixture(scope='module')
def all_foos(django_db_blocker):
    with django_db_blocker.unblock():
        yield Foo.objects.all()


def test_1(all_foos):
    assert all_foos.exists()

def test_2(all_foos, db):
    all_foos.delete()
    assert not Foo.objects.exists()

def test3(all_foos):
    assert all_foos.exists()

test_2运行后,从all_foos保存在session中的queryset会为空,导致test_3失败:

test_foo.py::test_1 PASSED                                                           [ 33%]
test_foo.py::test_2 PASSED                                                           [ 66%]
test_foo.py::test_3 FAILED                                                           [100%]

========================================= FAILURES ========================================
__________________________________________ test_3 _________________________________________

all_foos = <QuerySet []>

    def test_3(all_foos):
>       assert all_foos.exists()
E       assert False
E        +  where False = <bound method QuerySet.exists of <QuerySet []>>()
E        +    where <bound method QuerySet.exists of <QuerySet []>> = <QuerySet []>.exists

test_foo.py:28: AssertionError

结果:如果您不想引入可以在测试中更改的全局状态,则永远不要在 session 范围内存储引用。从数据库中查询数据并返回副本或序列化数据等。

安全使用示例:

@pytest.fixture(scope='session')
def foo_names(django_db_blocker):
    with django_db_blocker.unblock():
        names = list(Foo.objects.values_list('name', flat=True))
    return names

关于python - pytest ScopeMismatch 错误 : how to use fixtures properly,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51783757/

相关文章:

python - ValueError:字典包含不在字段名中的字段:无

python - 在 Python 脚本中解析 JSON 数据时遇到问题

python - Mechanize :第一种形式有效,然后是 "unknown GET form encoding type ' utf- 8'"

python - Django PyTest - 不允许数据库访问即使使用 django_db 和装置也会出错?

django - 通过 pytest 断言 HTMLEqual()

python - Axis 类 - 以给定单位明确设置 Axis 的大小(宽度/高度)

python - 如何安装py.test?

python - 从兄弟目录导入模块以与 py.test 一起使用

pytest - 如何将 pytest 迁移到 bazel py_test?

python - 安装了 pytest 但在 bash 中运行 `pytest` 返回 `not found`