Search code examples
pythondjangounit-testingdjango-unittest

Django - Testing Carrying Over POST Variables


I am working on doing some code coverage unit testing for my Django application when I ran into this interesting predicament.

tests/test_views.py

class TestLogin(TestCase):
    def setUp(self):
        self.client = Client()
        self.url = reverse('account:login')
        self.template = 'account/login.html'

    # This test works
    def test_POST_invalid_login(self):
        response = self.client.post(self.url, {
            'username': 'foo',
            'password': 'bar'
        })
        self.assertEqual(response.status_code, 401)
        self.assertTemplateUsed(response, self.template)

    # This is not working as intended.
    def test_POST_no_data(self):
        with self.assertRaises(MultiValueDictKeyError):
            self.client.post(self.url, None)

views.py

class Login(View):
    form_class = LoginForm
    template = 'account/login.html'

    def get(self, request):
        form = self.form_class(None)
        context = {
            'form': form
        }

        return render(request, self.template, context)

    def post(self, request):
        try:
            # Retrieve the username and password
            username = request.POST['username']
            password = request.POST['password']

            # Create a user object from authentication, or return None
            user = authenticate(username=username, password=password)

            # Check if user was created
            if user is not None:
                # Login the user to the website
                login(request, user)

                messages.success(request, 'Login successful! Welcome ' + user.first_name + ' ' + user.last_name)

                # Eventually have this go to profile pages.
                return redirect('home', permanent=True)
            else:
                messages.error(request, 'Login failed: Invalid User.')

        except MultiValueDictKeyError:
            messages.error(request, 'Login failed: Invalid User.')

        # Clear the form since the login attempt failed.
        form = self.form_class(None)

        context = {
            'form': form
        }

        return render(request, self.template, context, status=401)

The first test, the test_POST_invalid_login sends the data as it suppose to, but the second test, test_POST_no_data should not send any data, but the view still recognizes the foo and bar username and password values thus not catching the exception as it should. I originally written the no data test first and it worked originally. I tried moving the test method above the invalid login test, and it will still fail.

Any suggestions?


Solution

  • Your test_POST_no_data test asserts that MultiValueDictKeyError is raised, but this exception is caught in the view, so the view will never raise it. So, test_POST_no_data should be similar to test_POST_invalid_login, i.e. should assert the same things.

    You can check this is true by writing an empty raise statement inside the except MultiValueDictKeyError: block. This way, you'll re-raise the same exception and your test will pass.