Как тестировать Django, Django Rest-Framework и Djoser

В этой статье мы рассмотрим, как протестировать djoser при использовании активации электронной почты для ваших пользователей.

Требования

  • Вы уже используете djoser в своем приложении.

Итак, вы используете django, django restframework и djoser с email активацией, но испытываете трудности с тестированием аутентификации? Что ж, давайте немного разберемся с этой проблемой.

Прежде всего, нам нужно разделить наш файл настроек на production, development и testing. Структура выглядит следующим образом

settings/
      __init__.py
      base.py
      production.py
      development.py
      testing.py
Вход в полноэкранный режим Выход из полноэкранного режима

Первое, что мы должны сделать, это создать папку внутри вашего основного проекта под названием settings. Внимательно отнеситесь к названию папки, оно должно быть точно таким же, как settings.py, но без .py.

Далее мы создаем следующие файлы

__init__.py
base.py
production.py
development.py
testing.py
Вход в полноэкранный режим Выход из полноэкранного режима

Эти файлы используются в соответствии с названием, в разработке настройки django используют development.py, в тестировании — testing.py и так далее.

Теперь давайте скопируем общие настройки для всего проекта в base.py. Вы сами должны определить, что является общим для всего проекта.

В моем случае, для base.py я поместил следующее

BASE_DIR
SECRET_KEY # read from environment variable
INSTALLED_APPS
MIDDLEWARE
ROOT_URLCONF
TEMPLATES
WSGI_APPLICATION
AUTH_PASSWORD_VALIDATORS
LANGUAGE_CODE
TIME_ZONE
USE_I18N
USE_TZ
STATIC_URL
DEFAULT_AUTO_FIELD
AUTH_USER_MODEL
# emial related parameter are read from env in my case
EMAIL_BACKEND
EMAIL_HOST
EMAIL_PORT
EMAIL_HOST_USER
EMAIL_HOST_PASSWORD
EMAIL_USE_TLS
REST_FRAMEWORK # depends on your choice of drf settings
DJOSER # common setting only we will extend it in other files
Войти в полноэкранный режим Выйти из полноэкранного режима

для production.py

from .base import *

DEBUG = False
ALLOWED_HOSTS = ['yoursite.com'] # up to you choice what to allow
DATABASES # i use different db for prod
DJOSER.update({
....
}) # i use different conf for prod 
Войти в полноэкранный режим Выйти из полноэкранного режима

для development.py

from .base import *

DEBUG = True
ALLOWED_HOSTS = ['*'] # up to you choice what to allow
DATABASES # i use different db for dev
DJOSER.update({
....
}) # i use different conf for dev
Войти в полноэкранный режим Выход из полноэкранного режима

для testing.py мы рассмотрим этот файл подробнее, а пока

from .base import *

DEBUG = True
ALLOWED_HOSTS = ['*'] # up to you choice what to allow
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
DATABASES # i use different db for test
DJOSER.update({
....
}) # i use different conf for test
Войти в полноэкранный режим Выйти из полноэкранного режима

Теперь последний файл __init__.py.
этот файл динамически загружает настройки

import os

from . import base

enviroment = os.environ.get('ENVIROMENT', 'development')

if enviroment == 'production':
    from .production import *
elif enviroment == 'testing':
    from .testing import *
else:
    from .development import *
Войти в полноэкранный режим Выйти из полноэкранного режима

Djoser использует различные классы сериализаторов для различных целей, но мы сосредоточимся только на Email сериализаторах, посмотрите документацию.

Итак, теперь давайте напишем файл testing.py.

from .base import *
DEBUG = True
ALLOWED_HOSTS = ['*']
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
DATABASES = {
    # 'default': {
    #     'ENGINE': 'django.db.backends.sqlite3',
    #     'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    # }
    # for postgresql: i use postgres 
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'djoser',
        'USER': 'djoser',
        'PASSWORD': 'djoser',
        'HOST': 'localhost',
        'PORT': '5432',
    }

    # for mysql:
    # 'default': {
    #     'ENGINE': 'django.db.backends.mysql',
    #     'NAME': 'djoser',
    #     'USER': 'djoser',
    #     'PASSWORD': 'djoser',
    #     'HOST': 'localhost',
    #     'PORT': '3306',
    # }
}

DJOSER_EMAIL = {
    'activation': 'your-authentication-app.email.ActivationEmail'
}

if 'EMAIL' in DJOSER:
    DJOSER['EMAIL'].update(DJOSER_EMAIL)
else:
    DJOSER['EMAIL'] = DJOSER_EMAIL

Вход в полноэкранный режим Выход из полноэкранного режима

если вы заметили в

DJOSER_EMAIL = {
    'activation': 'your-authentication-app.email.ActivationEmail'
}
Вход в полноэкранный режим Выход из полноэкранного режима

мы переопределим сериализатор активации электронной почты с помощью your-authentication-app.email.ActivationEmail, но он еще не существует, поэтому давайте создадим его сейчас.

В вашем приложении аутентификации создайте файл email.py и поместите в него нижеприведенный сериализатор, и давайте поговорим об этом.

from django.contrib.auth.tokens import default_token_generator

# djoser imports
from templated_mail.mail import BaseEmailMessage
from djoser import utils
from djoser.conf import settings

EMAILS = {}

class ActivationEmail(BaseEmailMessage):
    """Email Activation Token Generator
    """
    template_name = "email/activation.html"

    def get_context_data(self):
        # ActivationEmail can be deleted
        context = super().get_context_data()
        user = context.get("user")
        context["uid"] = utils.encode_uid(user.pk)
        context["token"] = default_token_generator.make_token(user)
        context["url"] = settings.ACTIVATION_URL.format(**context)
        uid, token = context['uid'], context['token']
        # here we store all the requested tokens in a dictionary for later use
        EMAILS[user.email] = {'uid': uid, 'token': token}
        return context
Вход в полноэкранный режим Выход из полноэкранного режима

Единственное, что мы добавляем из сериализатора djoser, это

EMAIL = {}

...

EMAILS[user.email] = {'uid': uid, 'token': token}
Вход в полноэкранный режим Выход из полноэкранного режима

Они используются для хранения электронной почты, отправленной с нашего сервера.

ПРИМЕЧАНИЕ: мы используем EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend', потому что во время тестирования мы не хотим, чтобы django отправлял реальные письма.

Теперь мы собираемся протестировать наших пользователей. Вот несколько примеров для тестирования, но в то же время вы сами можете решить, что тестировать

from django.urls import reverse
from django.contrib.auth import get_user_model
from rest_framework import status
from rest_framework.test import APITestCase

User = get_user_model()

class UserViewSetTest(APITestCase):

    def setUp(self):
        """
        Set up method which is used to initialize before any test run.
        """
        self.user_info = self.generate_user_info()

    def generate_user_info(self):
        """Generate user data for new user.
        Returns:
            Dict: dictionary of the test user data.
        """
        return {
            "first_name": "fake.first_name()",
            "last_name": "fake.last_name()",
            "username": "fake.user_name()",
            "password": "fake.password()",
        }

    def test_create_user(self):
        """
        Test for creating users using API.
        """
        url = reverse("user-list")
        response = self.client.post(
            url,
            self.user_info,
        )
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        user = User.objects.get(id=response.data['id'])
        self.assertEqual(user.email, self.user_info["email"])
        self.assertEqual(user.username, self.user_info["username"])
        # self.assertEqual(user.ssn, self.user_info["ssn"])
        self.assertTrue(user.password is not self.user_info["password"])
        self.assertTrue(user.is_deleted is not True)
        self.assertTrue(user.father_first_name is None)
        self.assertTrue(user.mother_first_name is None)
        self.assertTrue(user.password is not None)
        self.assertTrue(user.birth_date is not None)

    def test_get_token(self):
        """
        This test is used to test the login API. getting token and testing the token.
        """
        # Create a new user to login
        user_info = self.generate_user_info()
        new_user = self.client.post(
            reverse("user-list"),
            user_info,
        )
        self.assertEqual(new_user.status_code, status.HTTP_201_CREATED)

        # Activation of User
        from authentications.email import EMAILS

        activation_url = reverse('user-activation')
        activation_data = EMAILS[user_info["email"]]
        self.client.post(activation_url, activation_data)

        url = reverse("jwt-create")
        data = {
            "username": user_info["username"],
            "password": user_info["password"],
        }
        response = self.client.post(url, data)

        self.assertTrue(response.status_code, status.HTTP_200_OK)
        self.assertTrue(response.data["access"] is not None)

    def test_get_user(self):
        """
        This test for retrieving single user using API.
        """

        # Create a new user to login
        new_user = self.client.post(
            reverse("user-list"),
            self.user_info,
        )
        self.assertEqual(new_user.status_code, status.HTTP_201_CREATED)

        # Activate User
        from authentications.email import EMAILS

        activation_url = "http://127.0.0.1:8000/auth/users/activation/"
        activation_data = EMAILS[self.user_info["email"]]
        self.client.post(activation_url, activation_data)

        # Get token
        url = reverse("jwt-create")
        data = {
            "username": self.user_info["username"],
            "password": self.user_info["password"],
        }

        response = self.client.post(url, data)
        self.assertTrue(response.status_code, status.HTTP_200_OK)

        # Get user
        token = response.data["access"]
        self.client.credentials(HTTP_AUTHORIZATION=f"JWT {token}")

        url = reverse('user-list', kwargs={'id':new_user.data["id"]})
        get_user = self.client.get(url)

        self.assertEqual(get_user.status_code, status.HTTP_200_OK)
        self.assertEqual(get_user.data["id"], new_user.data["id"])
        self.assertEqual(get_user.data["email"], new_user.data["email"])

        test_user = self.client.post(
            reverse("user-list"),
            self.generate_user_info(),
        )
        url = url = reverse('user-list', kwargs={'id': test_user.data['id'] })
        get_user = self.client.get(url)
        self.assertEqual(get_user.status_code, status.HTTP_404_NOT_FOUND)
Войти в полноэкранный режим Выход из полноэкранного режима

Есть разные виды тестов, но главная цель этого блога — дать вам знать, как протестировать djoser с включенной активацией электронной почты.

Надеюсь, это поможет вам.

Наслаждайтесь.

Оцените статью
devanswers.ru
Добавить комментарий