I have a django email verification app that sends email with activation url that contains encoded user pk and a token, then when app recieves the correct data in a url it sets user.is_active
boolean value to True
. I've written a test that is supposed to create a user and send a get request with it's encoded pk and a token, but it fails to activate my user even though the url is correct (as you will be able to see below)
views.py contains signup function and verification class based view.
def signup(request):
if request.method == 'POST':
form = SignupForm(request.POST)
if form.is_valid():
# ... process form data
user.save()
# Verification
email_subject = 'Activate your followerr account'
domain = get_current_site(request).domain
user_id = urlsafe_base64_encode(force_bytes(user.pk))
link = reverse('activate', kwargs={
'user_id': user_id,
'token': token_generator.make_token(user),
})
activate_url = 'http://' + domain + link
email_body = 'Hello ' + user.name + \
' please use this link to verify your account\n' + activate_url
email = EmailMessage(
email_subject,
email_body,
'[email protected]',
[user.email],
)
email.send()
return redirect('login')
else:
form = SignupForm()
return render(request, 'signup.html', {'form': form})
class VerificationView(View):
def get(self, request, user_id, token):
try:
id = urlsafe_base64_decode(force_text(user_id))
user = User.objects.get(pk=id)
if not token_generator.check_token(user, token):
return HttpResponse('login failed')
if user.is_active:
return redirect('login')
user.is_active = True
user.save()
return redirect('login')
except Exception as ex:
pass
return redirect('login')
Here in my test I create a new user with post request and check if the email exists, then I grab the token from the only email in the outbox (not the best way to do this I'm sure but it works right now), get the user_id and encoode it for the url and send a get request to the view which should find the user with the user_id, see if token matches the user and set user.is_active
to True
and redirect to login
page, however my user.is_active
value still remains False
after all of this is done. Here is the test:
tests.py
def test_signup_email_confirmation(self):
response = self.client.post('/signup/', data={'email': '[email protected]',
'name': 'test1',
'gender': True,
'password1': 'test1',
'password2': 'test1'}, follow=True)
self.assertEqual(len(mail.outbox), 1)
for i in mail.outbox:
token = i.body.split(' ')[-1]
print(i.body) # <-- the entire email body with the activation url
token = token[38:-1]
user = User.objects.get(email='[email protected]')
user_id = urlsafe_base64_encode(force_bytes(user.pk))
activation_url = reverse('activate', kwargs={'user_id': user_id, 'token': token})
activation_url = 'http://testserver' + activation_url
res = self.client.get(activation_url, follow=True)
print(activation_url)
print(token_generator.check_token(user, token))
print(res.status_code)
print(user.is_active)
self.assertEqual(res.status_code, 200)
self.assertRedirects(response, '/login/')
I've already tried using activation_url
without the 'http://testserver'
part.
Results of the print statements are here:
Hello test1 please use this link to verify your account
http://testserver/activate/Mg/ad1ap1-887a3ff4466d2e1d098603ddad24355e/
http://testserver/activate/Mg/ad1ap1-887a3ff4466d2e1d098603ddad24355e/
200
True
False
As you can see the url's match but is_active
value remains False
.
The reason this is happening is because the user
object in your test class was loaded from the database when is_active
was False
. When the view sets is_active=True
and saves the user, the test method has no way of knowing that the instance has modified.
So, now the user
instance in the test method still has old value.
If you want to test the modified fields, you must ask Django to refresh the object after saving:
user.refresh_from_db()
print(user.is_active)