Search code examples
djangopython-3.xdjango-formsdjango-testing

How to plug the reponse of a django view GET request into the same view as a POST request during testing?


I have a Django function-based form view that initializes a form with default data on a GET request, and saves the model object on a POST request:

def copy(request, ann_id):
    new_ann = get_object_or_404(Announcement, pk=ann_id)
    new_ann.pk = None  # autogen a new primary key (quest_id by default)
    new_ann.title = "Copy of " + new_ann.title
    new_ann.draft = True
    new_ann.datetime_released = new_ann.datetime_released + timedelta(days=7)

    form = AnnouncementForm(request.POST or None, instance=new_ann)
    if form.is_valid():
        new_announcement = form.save(commit=False)
        new_announcement.author = request.user
        new_announcement.datetime_created = timezone.now()
        new_announcement.save()
        form.save()

        return redirect(new_announcement)

    context = {
        "title": "",
        "heading": "Copy an Announcement",
        "form": form,
        "submit_btn_value": "Create",
    }
    return render(request, "announcements/form.html", context)

I can't figure out how to test the form.is_valid() branch when the form is posted, without manually providing the form data to self.client.post(url, form_data) in my view.

Here' what I'm trying:

test_views.py

class AnnouncementViewTests(TestCase):

    def setUp(self):
        self.client = ...
        ... etc

    def test_copy_announcement(self):
        # log in a teacher
        success = self.client.login(username=self.test_teacher.username, password=self.test_password)
        self.assertTrue(success)

        # hit the view as a get request first, to load a copy of the announcement in the form
        response = self.client.get(
            reverse('announcements:copy', args=[self.test_announcement.id]),
        )
        self.assertEqual(response.status_code, 200)

        # The form in this response should be valid, and should bw
        # accepted on a post request, 
        # that is what I am testing in this integration test.

        form_data = response.how_do_get_the_form_data() # ???????

        response = self.client.post(
            reverse('announcements:copy', args=[self.test_announcement.id]),
            data=form_data
        )

        # Get the newest announcement just made in the post request
        new_ann = Announcement.objects.latest('datetime_created')

        self.assertRedirects(
            response, 
            new_ann.get_absolute_url()
        )

What I want to actually test is that the result of the get provides valid default data for the form that can then be submitted via post request.

But I can't figure out how to access the form data resulting from the get request, so I can then feed it into the form_data provided to the post request.

EDIT

I found the location of the form in the get response, but I have no idea how to get that in code.

enter image description here


Solution

  • You can access the response form in this way:

    response.context['form']
    

    From here you can build your payload in this way:

    retrieved_instance = response.context['form'].instance
    form_data = dict(title=retrieved_instance.title, ... <all the other fields>)
    response = self.client.post(
                reverse('announcements:copy', args=[self.test_announcement.id]),
                data=form_data)
            )
    

    This is not like resubmitting the page but is very similar because you're resubmitting the same form.

    Actually, your test seems more like an e2e test (when speaking about integrations and e2e there is some ambiguity let's say), for this reason, if I were you, I would switch "tool" and use selenium to simulate a user interaction from the beginning (open an existing announcement) to the end, pressing the submit button on your web page.

    Only in this way what you're submitting is the "real" response of the "get"

    If you are new to this kind of test, you can find here a simple tutorial https://lincolnloop.com/blog/introduction-django-selenium-testing/ to understand the main concepts.