Модульный тест Django - использование factory_boy build() для модели с отношением "Многие ко многим"
Я работаю над написанием модульных тестов для проекта DRF, используя pytest и factory_boy. У меня возникают проблемы с отношениями "многие ко многим". В частности, когда я пытаюсь использовать .build() в своих модульных тестах, DRF пытается получить доступ к полю M2M, для которого требуется сохраненный объект, что приводит к ошибкам.
tests_serializers.py
def test_serialize_quality_valid_data(self):
user = UserFactory.build()
quality = QualityFactory.build(created_by=user)
serializer = QualitySerializer(quality)
data = serializer.data
assert data["num"] == quality.num
ошибка:
FAILED quality/tests/tests_serializers.py::TestQualitySerializer::test_serialize_quality_valid_data - ValueError: "<Quality: Quality object (None)>" needs to have a value for field "id" before this many-to-many relationship can be used.
model.py
class QualityTag(ExportModelOperationsMixin("quality_tag"), models.Model):
name = models.CharField(max_length=64, unique=True)
description = models.TextField()
class Quality(ExportModelOperationsMixin("quality"), models.Model):
num = models.IntegerField()
title = models.CharField(max_length=64)
...
tags = models.ManyToManyField(QualityTag, related_name="qualities", blank=True)
factories.py
class QualityTagFactory(DjangoModelFactory):
class Meta:
model = QualityTag
name = factory.Sequence(lambda n: f"Quality Tag {n}")
class QualityFactory(factory.django.DjangoModelFactory):
class Meta:
model = Quality
num = factory.Faker("random_int", min=1, max=999)
@factory.post_generation
def tags(self, create, extracted, **kwargs):
if not create:
return
if extracted:
for tag in extracted:
self.tags.add(tag)
serializers.py
class QualitySerializer(serializers.ModelSerializer):
tags = QualityTagDetailSerializer(many=True)
created_by = UserProfileSerializer()
updated_by = UserProfileSerializer()
class Meta: model = Quality
fields = "__all__"
read_only_fields = ["quality_num", "tags", "created_by", "updated_by"]
Мне посоветовали переключиться на .create() вместо .build(), но я бы предпочел, чтобы это было чисто модульным тестом, если это возможно (я полагаю, что это может стать интеграционным тестом, если я использую build()?) Я попытался преобразовать его с помощью factory_boy .create(), однако я хочу сохранить его как простой модульный тест.
Я попытался преобразовать его с помощью factory_boy .create(), однако я хочу сохранить его как простой модульный тест.
Насколько я знаю, когда вы используете factory_boy.build(), объект существует только в памяти без идентификатора базы данных. Для правильной работы полей M2M в Django требуется сохраненный объект (с идентификатором). Итак, вы можете протестировать свою логику сериализатора, не обращаясь к базе данных, используя поле M2M, что-то вроде этого:
def test_serialize_quality_with_tags(self):
user = UserFactory.build()
quality = QualityFactory.build(created_by=user)
# Create tag objects in memory
tag1 = QualityTagFactory.build(name="Tag 1")
tag2 = QualityTagFactory.build(name="Tag 2")
mock_tags_data = [tag1, tag2]
with patch.object(quality, 'tags') as mock_tags:
# Mock the M2M field to return our test data
mock_tags.all.return_value = mock_tags_data
mock_tags.__iter__ = lambda x: iter(mock_tags_data)
serializer = QualitySerializer(quality)
data = serializer.data
assert data["num"] == quality.num
assert len(data["tags"]) == 2
assert data["tags"][0]["name"] == "Tag 1"
проверьте это для получения более подробной информации: https://factoryboy.readthedocs.io/en/stable/reference.html#id9