使用 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
我正在尝试通过在测试之间缓存对象来加快我的单元测试。
我知道测试应该相互隔离。但是创建我的对象的行为是昂贵的,没有必要一遍又一遍地创建相同的对象。
最初,我认为我可以从 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