Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test login process?

I am developing a web application using Django 1.6 where I require users to log in using my login form. I want to write a test case that tests the procedure of a user logging in.

I did succeed to get a working login page, meaning I was able to log in. The following steps explain my setup. Writing a test case for this procedure did however fail. At this point I'm neither able to spot an error in my test, nor sure whether this approach makes sense before all. Can someone help me?

Update: In the meantime I have noticed, that my form error message was too unspecific. The issue appears to be caused by empty form fields that fail some form validation step. When I add the errors {{ form.errors }} to the template, the response contains the following (formatted a little less nicely):

<ul class="errorlist">
  <li>username
    <ul class="errorlist">
      <li>This field is required.</li>
    </ul>
  </li>
  <li>password
    <ul class="errorlist">
      <li>This field is required.</li>
    </ul>
  </li>
</ul>

I'm still not sure how to test this procedure most reasonably. I'm quite new to Django and the philosophy of it's testing framework. May it be, that this issue falls not directly into the scope of the Django testing framework, where models, and views, and forms are (seemingly) intended to be tested rather seperated from each other? Does this test rather fall into the realm of an independent test suite doing black-box/functional testing, e.g., using Selenium?


I started like this:

  1. Started a fresh project, like this:

    django-admin.py startproject login_test
    
  2. added an entry to the login_test/urls.py, like this:

    urlpatterns = patterns('',
        url(r'^login/$', 'django.contrib.auth.views.login'),
        url(r'^admin/', include(admin.site.urls)),
    )
    
  3. made up a template at /home/moooeeeep/login_test/templates/registration/login.html, like this:

    {% if form.errors %}
    <p>Your username and password didn't match. Please try again.</p>
    {% endif %}
    {% if not user.is_active %}
    <form method="post" action="{% url 'django.contrib.auth.views.login' %}">
      {% csrf_token %}
      {{ form.as_p }}
      <button type="submit">Login</button>
      <input type="hidden" name="next" value="{% url 'django.contrib.auth.views.login' %}" />
    </form>
    {% else %}
    <p>You are logged in as <strong>{{ user.username }}</strong>.</p>
    {% endif %}
    
  4. added the template directory to the TEMPLATE_DIRS in the login_test/settings.py, like this:

    TEMPLATE_DIRS = (
        '/home/moooeeeep/login_test/templates/',
    )
    
  5. Sync'ed the database to get the auth-related tables, and added a superuser like this:

    python manage.py syncdb
    

If I did not miss a step, at this point I am able to log into my site. When logged in I get displayed my username, if not I see the form, if login fails it displays an error message in addition.


My test case however, after submitting correct post data, gets to see the failed login response, with the user not being logged in.

Here's my login_test/tests.py:

from django.contrib.auth.models import User
from django.contrib.auth import SESSION_KEY
from django.test import TestCase

class LogInTest(TestCase):
    def setUp(self):
        self.credentials = {
            'username': 'testuser',
            'password': 'secret'}
        User.objects.create_user(**self.credentials)
    def test_login(self):
        # login
        response = self.client.post('/login/', **self.credentials)      
        # should be logged in now, fails however
        self.assertTrue(response.context['user'].is_active)

Here's the output:

$ python manage.py test
Creating test database for alias 'default'...
F
======================================================================
FAIL: test_login (login_test.tests.LogInTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/moooeeeep/login_test/login_test/tests.py", line 16, in test_login
    self.assertTrue(response.context['user'].is_active)
AssertionError: False is not true

----------------------------------------------------------------------
Ran 1 test in 0.008s

FAILED (failures=1)
Destroying test database for alias 'default'...

Here's my login_test/settings.py:

import os
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '+6e-6t8dnl$75fx%=7y$+4ipo&i=kvw(p*963p37)7o$1-)30u'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
TEMPLATE_DEBUG = True
INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
)
MIDDLEWARE_CLASSES = (
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
)
ROOT_URLCONF = 'login_test.urls'
WSGI_APPLICATION = 'login_test.wsgi.application'
DATABASES = {
    'default': {
    'ENGINE': 'django.db.backends.sqlite3',
    'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}
TEMPLATE_DIRS = (
    '/home/moooeeeep/login_test/templates/',
)
like image 558
moooeeeep Avatar asked Mar 17 '14 14:03

moooeeeep


People also ask

How do you write a test case for login form?

Functional Test Scenarios for Login Page Check if the password is in masked form when typed in the password field. Check if the password can be copy-pasted or not. Verify that the user is able to login by entering valid credentials and clicking on the 'Login' button.


2 Answers

I found out that my passing of the post parameters was wrong (the dictionary must not be unpacked). I also needed to learn, that I needed to follow the redirect issued by the post request after sending the data. Then my login test worked (although the page that was target of the redirection does not exist yet).

Here's my updated test case:

from django.contrib.auth.models import User
from django.test import TestCase

class LogInTest(TestCase):
    def setUp(self):
        self.credentials = {
            'username': 'testuser',
            'password': 'secret'}
        User.objects.create_user(**self.credentials)
    def test_login(self):
        # send login data
        response = self.client.post('/login/', self.credentials, follow=True)
        # should be logged in now
        self.assertTrue(response.context['user'].is_active)

Here's the output:

$ python manage.py test
Creating test database for alias 'default'...
.
----------------------------------------------------------------------
Ran 1 test in 0.196s

OK
Destroying test database for alias 'default'...
like image 87
moooeeeep Avatar answered Sep 22 '22 10:09

moooeeeep


You don't want to be checking "is_active" boolean, you want to be checking

response.context['user'].is_authenticated

see: https://docs.djangoproject.com/en/dev/topics/auth/default/#authentication-in-web-requests

and: https://docs.djangoproject.com/en/dev/ref/contrib/auth/

like image 37
nkhumphreys Avatar answered Sep 21 '22 10:09

nkhumphreys



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!