Search code examples
pythondjangodjango-testing

Why is my test function not activating the user?


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.


Solution

  • 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)