Django тестирование UpdateView - у объекта нет атрибута 'object'

Я пытаюсь написать тест для созданного мной UpdateView.

Мое представление выглядит следующим образом:

class MutantUpdateView(UpdateView):
    context_object_name = "mutant"
    fields = ["mutation_level"]
    model = Mutant
    pk_url_kwarg = "mutant_id"
    template_name_suffix = "_update_form"

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["admin"] = get_object_or_404(
            Person, id=self.kwargs["admin_id"]
        )
        context["backstory"] = get_object_or_404(
            Backstory, id=self.kwargs["backstory_id"]
        )
        context["evidences"] = self.object.evidences.all()
        return context

    def get_form(self, form_class=None):
        form = super().get_form(form_class)
        mutant = self.object
        mutation_levels = (
            mutant.expectation.mutation_levels.all()
        )
        form.fields["mutation_level"].queryset = mutation_levels
        return form

    def form_valid(self, form):
        form.instance.updated_by = = get_object_or_404(
            Person, id=self.request.user.person_id
        )
        return super().form_valid(form)

    def get_success_url(self, **kwargs):
        return reverse_lazy(
            "mutants:mutant-update",
            kwargs={
                "admin_id": self.kwargs["admin_id"],
                "backstory_id": self.kwargs["backstory_id"],
                "mutant_id": self.kwargs["mutant_id"],
            },
        )

Мой тест выглядит следующим образом ( согласно документации)

def test_form_valid_posts_appropriately(self):
    new_level = MutantLevelFactory(short_description="Next Mutant Level")
    self.mutant.expectation.mutation_levels.add(new_level)
    data = {
        "created_by": self.admin_person.id,
        "updated_by": self.admin_person.id,
        "backstory": self.mutant.backstory.id,
        "expectation_level": new_level,
        "expectation": self.mutant.expectation.id,
    }
    kwargs = {
        "admin_id": self.admin_person.id,
        "backstory_id": self.mutant.backstory.id,
        "mutant_id": self.mutant.id,
    }
    request = self.factory.get(
        reverse("mutants:mutant-update", kwargs=kwargs)
    )
    request.user = self.admin_user  # simulate admin user logged in
    self.view.setup(request)
    context = self.view.get_context_data()
    self.assertIn('backstory', context)
    self.assertFalse(context["form"].errors)
    self.assertEqual(response.status_code, 302)

Выдает ошибку:

    def get_form(self, form_class=None):
        form = super().get_form(form_class)
>       mutant = self.object
E       AttributeError: 'MutantUpdateView' object has no attribute 'object'

Модель выглядит следующим образом:

class Mutant(models.Model):
    id = models.BigAutoField(primary_key=True)
    backstory = models.ForeignKey(
        Backstory, on_delete=models.PROTECT, related_name="%(class)ss"
    )
    expectation = models.ForeignKey(
        Expectation, on_delete=models.PROTECT, related_name="%(class)ss"
    )
    mutation_level = models.ForeignKey(
        MutationLevel,
        on_delete=models.PROTECT,
        null=True,
        blank=True,
        related_name="%(class)ss",
    )

Каким образом лучше всего тестировать базовый Django UpdateView? Это лучший способ или мне следует использовать Client() и сделать тест интеграционного типа? В конечном счете, я хотел бы и юнит-тестировать, и интегрировать это представление - если это уместно.

Я пробовал изменять код различными способами, такими как:

response = MutantUpdateView.as_view()(request, **kwargs)

но и это не сработало.

The problem is not the view, but testing the view. Your test aims to "mimic" the code flow of the view, but the object is set in the .get(…) method [classy-Django]. You likely can implement that too, but if you later change the view, or you add a mixin, etc. That will result in fixing all tests.

Обычно для этого используется django test client [Django-doc]: инструмент, который будет имитировать запрос и передавать его через представление:

from django.test import TestCase


class MyTestCase(TestCase):
    def test_form_valid_posts_appropriately(self):
        self.client.force_login(self.admin_user)
        new_level = MutantLevelFactory(short_description='Next Mutant Level')
        self.mutant.expectation.mutation_levels.add(new_level)
        data = {
            'created_by': self.admin_person.id,
            'updated_by': self.admin_person.id,
            'backstory': self.mutant.backstory.id,
            'expectation_level': new_level,
            'expectation': self.mutant.expectation.id,
        }
        kwargs = {
            'admin_id': self.admin_person.id,
            'backstory_id': self.mutant.backstory.id,
            'mutant_id': self.mutant.id,
        }
        response = self.client.post(
            reverse('mutants:mutant-update', kwargs=kwargs), data
        )
        self.assertIn('backstory', response.context)
        self.assertFalse(response.context['form'].errors)
        self.assertEqual(response.status_code, 302)
Вернуться на верх