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

Требования

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

Итак, вы используете django, django restframework и djoser с активацией по электронной почте, но вам трудно проверить аутентификацию? хорошо, давайте немного исправим эту проблему.

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

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 *
Войти в полноэкранный режим

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

Джосер использует разные классы сериализаторов для разных целей, но мы сосредоточимся только на Сериализаторы электронной почты проверить документы

Итак, теперь давайте напишем 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 = "
        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 с включенной активацией по электронной почте.

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

Наслаждаться.