使用 pytest-django 在测试之间缓存对象

Caching objects inbetween tests using pytest-django

我正在尝试通过在测试之间缓存对象来加快我的单元测试。

我知道测试应该相互隔离。但是创建我的对象的行为是昂贵的,没有必要一遍又一遍地创建相同的对象。

最初,我认为我可以从 setup_class 调用一个固定装置,但显然这不起作用。所以我刚刚编写了一个简单的函数来在每次测试开始时调用。这 nearly 有效。但是,相关对象是空的。

这是一些代码:

models.py:

class Author(models.Model):
    name = models.CharField(max_length=100)

class Book(models.Model):
    title = models.CharField(max_length=100)
    author = models.ForeignKey(Author, related_name="books")

tests.py:

import factory
import pytest
from faker import Faker
from django.db import models

from models import Book, Author

fake = Faker()

class AuthorFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = Author
    name = factory.Faker('name')

class BookFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = Book
    title = factory.Faker('sentence')
    author = factory.SubFactory(AuthorFactory)

def make_an_author(**kwargs):

    n_books = kwargs.pop("n_books", 10)

    author = AuthorFactory.create(**kwargs)
    for i in range(n_books):
        BookFactory.create(author=author)

    return author

@pytest.mark.django_db
class TestAuthor:

    N_AUTHORS = 10
    cached_authors = []

    def cache_authors(self, n_authors):
        current_n_authors = len(self.cached_authors)
        if current_n_authors < n_authors:
            self.cached_authors.extend(
                [
                    make_an_author(n_books=2)
                    for i in range(current_n_authors, n_authors)
                ]
            )

    def test_one(self, cache_authors):
        self.cache_authors(10)
        author = fake.choice(self.cached_authors)
        # THIS WORKS!
        assert author.books.count() != 0

    def test_two(self, cache_authors):
        self.cache_authors(10)
        author = fake.choice(self.cached_authors)
        # THIS FAILS!
        assert author.books.count() != 0

每次第一次测试后,Author & Books 之间的反向关系为空。

知道为什么吗?

这里最好的选择可能是在缓存 Author 时预取 Book 对象。目前 Book 是通过 Author 上的 RelatedManager 按需查找的。该查找在第二次测试中不起作用,因为在最初缓存对象的第一次测试后事务被拆除。

你可以试试:

def make_an_author(**kwargs):

    n_books = kwargs.pop("n_books", 10)

    author = AuthorFactory.create(**kwargs)
    for i in range(n_books):
        BookFactory.create(author=author)

    # Prefetch the books before caching the author
    author = Author.objects.prefetch_related('books').get(pk=author.pk)

    return author