Модульный тест 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

Вернуться на верх