Reputation: 374
I am working on doing some code coverage unit testing for my Django application when I ran into this interesting predicament.
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)
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?
Upvotes: 0
Views: 675
Reputation: 374
Since I want to catch the exception in the code; I decided to refactor the no data test to check if it'll get the following assertions. Granted, I could remove the try/except blocks, and check if request.POST
has the following keys: username
and password
, but I want it to fail gracefully and let the app go back to the login page with a failed message instead of contouring with a unit test.
# Refactored, and working
def test_POST_no_data(self):
self.assertEqual(response.status_code, 401)
self.assertTemplateUsed(response, self.template)
I could bring up another question if this the best way to handle exceptions and testing exceptions, but that'll be another question.
Thanks, Iulia Chiriac, for the answer.
Upvotes: 0
Reputation: 76
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.
Upvotes: 2