Monday, May 25, 2015

I think I might have found a bug in a django test client - please help

Good day everyone.


I would like to ask you if this incorrect behaviour is something known.

First, I am using Django 1.8.2, Python 3.4.3, Python Tools for Visual Studio 2.2RC, VS2013, Windows 8.1, msysgit 1.9.5.

A made a unit test which was supposed to fail, and it did fail from the visual studio test runner, but it was successfully passed when I ran it from the command line using manage.py test command.


I run tests in command as follows: manage.py test --settings=monodemo.settings.dev_russ

This assert does not fail in Django test runner when my Form View (CBV) has a success_url='dashboard:dashboard':

self.assertEqual(self.client.session[SESSION_KEY], str(user.pk))


But it does fail in VS and it should because the return status code was 400 in any case. In visual studio unit tests, it was raising KeyError:'_auth_user_id'.

Also, I realised that I forgot to add 'dashboard' project into installed_apps but it was not affecting the result of the test.


My dependencies:

Django==1.8.2

psycopg2==2.6 (taken from here http://www.stickpeople.com/projects/python/win-psycopg/ and then manually installed into the virtual environment; direct download link: http://www.stickpeople.com/projects/python/win-psycopg/2.6.0/psycopg2-2.6.0.win32-py3.4-pg9.4.1-release.exe )

Unipath==1.1


Please advise if this is something to worry or if I have improperly configured something.

Thank you all in advance!

Ruslan (Russ) Bazhenov


I have attached pretty much all the related code down there. (Tried to exclude what was not related, though)


#monodemo/registration/tests/base.py

import django

from django.test import TestCase

from django.test.utils import setup_test_environment

from django.contrib.auth.models import User

class AbstractTestCase(TestCase):

    """Utility base class for all tests for this app as it contains helper functions"""

 

    @classmethod

    def setUpClass(cls):

        """One time Django setup sequence.

        It is needed for correct testing outside of django test runner

        For example in the UnitTest test runner or the one used in VS"""

        setup_test_environment()

        django.setup()

        super().setUpClass()

 

    def prepare_user(self, username):

        """Creates, saves and returns test user"""

 

        # An interesting observation

        # By default a user iscreated ACTIVE

        return User.objects.create_user(username=username,

                                        email='test@example.com',

                                        password='glassfish100500'

                                        )

 

#monodemo/registration/tests/test_views.py

from django.core.urlresolvers import reverse

from django.contrib.auth import SESSION_KEY

 

from registration.forms import UserLoginForm

from .base import AbstractTestCase

 

class LoginViewTests(AbstractTestCase):

    """Tests for the Registration app views."""

 

    def test_can_login(self):

        """Verifies that a user can authenticate"""

        user = super().prepare_user('Sucker')

        email = 'test@example.com'

        password = 'glassfish100500'

        response = self.client.post(reverse('home_page'), data={'email':email,

                                                                'password':password,

                                                                'remember_me':True

                                                                })

        self.assertEqual(self.client.session[SESSION_KEY], str(user.pk))

 

#monodemo/registration/views.py

from django.views.generic import FormView

from django.contrib.auth import login

 

from registration.forms import UserLoginForm

 

 

class LoginView(FormView):

    """User authentication view"""

    template_name = 'registration/registration_login.html'

    form_class = UserLoginForm

    success_url = 'dashboard:dashboard'

 

    def form_valid(self, form):

        """Logs a verified user in after form is validated"""

        user = form.get_user()

        login(self.request, user)

        return super().form_valid(form)

 

 

#monodemo/registration/forms.py

from django import forms

from django.contrib.auth.models import User

from django.contrib.auth import authenticate

 

 

class UserLoginForm(forms.Form):

    """Verifies login details"""

    email = forms.EmailField(widget=forms.TextInput(attrs={'class': 'text',

                                                           'placeholder': 'Email',

                                                           }))

    password = forms.CharField(max_length=128,

                               widget=forms.PasswordInput(attrs={'class': 'text',

                                                                 'placeholder': 'Password',

                                                                 }))

    remember_me = forms.BooleanField(required=False,

                                     label='remember me',

                                     widget=forms.CheckboxInput(attrs={'class': 'checkbox',}))

 

    def clean(self):

        """User verification. Other data does not require validation

        because it will be handled by authenticate()

        """

        cleaned_data = super(UserLoginForm, self).clean()

        email = cleaned_data.get("email")

        password = cleaned_data.get("password")

        try:

            user = User.objects.get(email=email)

        except (User.DoesNotExist, User.MultipleObjectsReturned):

            raise forms.ValidationError("The email address or password were incorrect")

        user = authenticate(username=user.username, password=password)

        if user is None:

            raise forms.ValidationError("The email address or password were incorrect")

        if user.is_active != True:

            raise forms.ValidationError("The email address or password were incorrect")

        # Dirty hack or fancy Python?

        def get_user():

            return user

        self.get_user = get_user

 

#monodemo/monodemo/urls.py

from django.conf.urls import include, url

from django.contrib import admin

 

from registration.views import LoginView

 

urlpatterns = [

    url(r'^$', LoginView.as_view(), name='home_page'),

    url(r'^auth/', include('registration.urls', namespace='registration')),

    url(r'^dashboard/', include('dashboard.urls', namespace='dashboard')),

    url(r'^admin/', include(admin.site.urls)),

]

 

#monodemo/registration/urls.py

from django.conf.urls import include, url

 

urlpatterns = [

    ]

 

#monodemo/dashboard/urls.py

from django.conf.urls import url

 

from .views import LoginView

 

urlpatterns = [

    url(r'^$', LoginView.as_view(), name='dashboard'),

]

 

#monodemo/dashboard/views.py

from django.views.generic import TemplateView

 

class LoginView(TemplateView):

    template_name = 'dashboard/base_dashboard.html'

 

#monodemo/monodemo/settings/base.py

import json

 

from django.core.exceptions import ImproperlyConfigured

from unipath import Path

 

BASE_DIR = Path(__file__).ancestor(3)

MEDIA_ROOT = BASE_DIR.child("media")

STATIC_ROOT = BASE_DIR.child("static")

STATIC_URL = "/static/"

STATICFILES_DIRS = (

    BASE_DIR.child("assets"),

    )

 

 

# Quick-start development settings - unsuitable for production

# See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/

 

# JSON-based secrets module

with open(BASE_DIR.child("monodemo", "settings", "secrets.json")) as f:

    secrets = json.loads(f.read())

 

def get_secret(setting, secrets=secrets):

    """Get the secret variable or return explicit exception."""

    try:

        return secrets[setting]

    except KeyError:

        error_msg = "Set the {0} environment variable".format(setting)

        raise ImproperlyConfigured(error_msg)

 

# SECURITY WARNING: keep the secret key used in production secret!

SECRET_KEY = get_secret("SECRET_KEY")

 

# SECURITY WARNING: don't run with debug turned on in production!

DEBUG = False

 

ALLOWED_HOSTS = []

 

 

# Application definition

 

INSTALLED_APPS = (

    'django.contrib.admin',

    'django.contrib.auth',

    'django.contrib.contenttypes',

    'django.contrib.sessions',

    'django.contrib.messages',

    'django.contrib.staticfiles',

    'registration'

)

 

MIDDLEWARE_CLASSES = (

    'django.contrib.sessions.middleware.SessionMiddleware',

    'django.middleware.common.CommonMiddleware',

    'django.middleware.csrf.CsrfViewMiddleware',

    'django.contrib.auth.middleware.AuthenticationMiddleware',

    'django.contrib.auth.middleware.SessionAuthenticationMiddleware',

    'django.contrib.messages.middleware.MessageMiddleware',

    'django.middleware.clickjacking.XFrameOptionsMiddleware',

    'django.middleware.security.SecurityMiddleware',

)

 

ROOT_URLCONF = 'monodemo.urls'

 

TEMPLATES = [

    {

        'BACKEND': 'django.template.backends.django.DjangoTemplates',

        'DIRS': (

            BASE_DIR.child("templates"),

            ),

        'APP_DIRS': True,

        'OPTIONS': {

            'context_processors': [

                'django.contrib.auth.context_processors.auth',

                'django.template.context_processors.request',

                'django.template.context_processors.debug',

                'django.template.context_processors.i18n',

                'django.template.context_processors.media',

                'django.template.context_processors.static',

                'django.template.context_processors.tz',

                'django.contrib.messages.context_processors.messages',

            ],

        },

    },

]

 

WSGI_APPLICATION = 'monodemo.wsgi.application'

 

 

# Database

# https://docs.djangoproject.com/en/1.8/ref/settings/#databases

 

DATABASES = {

    'default': {

        'ENGINE': 'django.db.backends.postgresql_psycopg2',

        'NAME': get_secret("NAME"),

        'USER': get_secret("USER"),

        'PASSWORD': get_secret("PASSWORD"),

    }

}

 

 

# Internationalization

# https://docs.djangoproject.com/en/1.8/topics/i18n/

 

LANGUAGE_CODE = 'en-GB'

 

TIME_ZONE = 'UTC'

 

USE_I18N = True

 

USE_L10N = True

 

USE_TZ = True

 

#monodemo/monodemo/settings/local.py

from .base import *

 

DEBUG = True

 

#monodemo/monodemo/settings/dev_russ.py

from .local import *

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscribe@googlegroups.com.
To post to this group, send email to django-users@googlegroups.com.
Visit this group at http://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/b9d700ec-504f-4d06-be43-cd418fb13c27%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

No comments:

Post a Comment