Рубрики
Без рубрики

Как покрыть приложение Django с помощью модульных тестов

По мере роста приложения труднее проверить его вручную. В этой статье Фил хорошо научит вас, как охватывать приложения Django автоматизированными модульными тестами, чтобы у вас всегда были уверенность, все функции все еще работают. Tagged с Python, Django, тесты.

Примечание. Этот учебник основан на навыках и коде, разработанных в Как создать API с Django Анкет Мы будем работать над тем же API Todo List, используя эту версию на GitHub Анкет

Прошлым летом я работал над веб -приложением Django с некоторыми из моих друзей. Однажды я завершил функцию и отправил ее другому парню для тестирования. Он ответил, что не может проверить новую функцию, потому что я непреднамеренно сломал функцию знака. Проблема заключалась в том, что это произошло не просто один раз, что -то вроде этого происходило каждую неделю. Основная проблема заключалась в том, что наш рабочий процесс не включал достаточного автоматического тестирования.

Что такое автоматическое тестирование?

Тестирование вашего кода является неотъемлемой частью программирования. В зависимости от вашего проекта, вы можете проверить свой код, просмотрев веб -страницу, играя через видеоигру или анализируя журналы вывода. Ручное тестирование является интенсивным временным и оставляет место для ошибок, поэтому большинство профессиональных программистов уделяют серьезное внимание автоматическому тестированию. Сегодня мы собираемся просмотреть общий тип автоматического тестирования, модульных тестов, чтобы рассмотреть, как автоматизированное тестирование может помочь нам в процессе разработки. Во-первых, мы напишем тесты на модели и представления существующего API, а затем практикуйте разработку, управляемую тестированием, добавив новую функцию.

Автоматизированное тестирование экономит время и улучшает качество программного обеспечения. Ручное тестирование быстро при запуске: просто запустите код и посмотрите, работает ли он. Тем не менее, со временем вы разрабатываете все больше и больше функций в приложении, превращая ручные тестирование очень интенсивного времени. Кроме того, вы можете забыть проверить определенную функцию. Правильная реализация автоматического тестирования охватывает все, каждый раз, за секунды. Наличие хорошего автоматического тестирования также облегчает для других людей понять ваш код и позволяет командам собирать вещи вместе, не беспокоясь о том, чтобы разбить функции друг друга.

Единый тест проверяет функциональность компонента индивидуально. Это самый низкий уровень тестирования; Это гарантирует, что каждый аспект программы работает в одиночку (интеграция и системные тесты проверяют компоненты вместе и их взаимодействия, но это выходит за рамки этого учебника). Например, в объектно-ориентированной программе вы будете писать модульные тесты для каждого объекта и, в зависимости от сложности, для отдельных методов. В Джанго мы проверяем каждую модель и просмотр.

Как работает модульное тестирование в Джанго?

Чтобы следовать этим уроку, клонировать проект из GitHub Анкет Следуйте тем же шагам, чтобы настроить проект как первые три абзаца Как создать API с Django Анкет

Когда вы используете Python Manage.py StartApp AppName Чтобы создать приложение Django, один из файлов Django создает в Имя приложения каталог называется tests.py Анкет Этот файл существует в домашних модульных тестах для моделей и других компонентов в приложении. По умолчанию файл содержит одну строку кода: от django.test Import Testcase Анкет Тестовый пример содержит несколько связанных тестов для одного и того же куска кода. Testcase это объект Django, который мы будем наследовать, чтобы создать собственные модульные тесты. У класса есть два метода, Настройка (Self) и слеза (я) , которые запускаются до и после отдельных функций тестирования, чтобы предоставить и очистить базу данных тестирования отдельно от любой базы данных, которую вы получаете с Python Manage.py Runserver . Откройте tests.py в Тодо Папка проекта для изучения кода.

class SigninTest(TestCase):

    def setUp(self):
        self.user = get_user_model().objects.create_user(username='test', password='12test12', email='test@example.com')
        self.user.save()

    def tearDown(self):
        self.user.delete()

    def test_correct(self):
        user = authenticate(username='test', password='12test12')
        self.assertTrue((user is not None) and user.is_authenticated)

    def test_wrong_username(self):
        user = authenticate(username='wrong', password='12test12')
        self.assertFalse(user is not None and user.is_authenticated)

    def test_wrong_pssword(self):
        user = authenticate(username='test', password='wrong')
        self.assertFalse(user is not None and user.is_authenticated)

Здесь мы проверяем функцию знака. Поскольку мы используем встроенные методы Django, если с базой данных ничего не сломано, это должно работать нормально, поэтому нам нужны только простые тесты: если представлена правильная информация, аутентикация, если неверная информация дана, не делайте. Этот тестовый пример позволяет нам увидеть еще несколько вещей о модульном тестировании в Джанго. Во -первых, все методы испытаний в тестовом случае должны начинаться с test_ Чтобы запустить, когда мы выполняем Python Manage.py Test командование Другие методы в тестовом примере считаются вспомогательными функциями. Другая важная часть заключается в том, что все методы испытаний должны занять я как аргумент, где я это ссылка на Testcase объект. Testcase , который мы наследуем для создания нашего класса, предоставляет методы утверждения для оценки логических. A Self.AssertSomething () Вызов проходит, если значения, передаваемые в виде аргументов, соответствуют утверждению, и не удается иначе. Метод испытаний проходит только в том случае, если каждое утверждение в методе проходит.

class TaskTest(TestCase):

    def setUp(self):
        self.user = get_user_model().objects.create_user(username='test', password='12test12', email='test@example.com')
        self.user.save()
        self.timestamp = date.today()
        self.task = Task(user=self.user,
                         description='description',
                         due=self.timestamp + timedelta(days=1))
        self.task.save()

    def tearDown(self):
        self.user.delete()

    def test_read_task(self):
        self.assertEqual(self.task.user, self.user)
        self.assertEqual(self.task.description, 'description')
        self.assertEqual(self.task.due, self.timestamp + timedelta(days=1))

    def test_update_task_description(self):
        self.task.description = 'new description'
        self.task.save()
        self.assertEqual(self.task.description, 'new description')

    def test_update_task_due(self):
        self.task.due = self.timestamp + timedelta(days=2)
        self.task.save()
        self.assertEqual(self.task.due, self.timestamp + timedelta(days=2))

Теперь мы проверяем нашу собственную модель: Задача объект определен в модели.py Анкет Чтобы настроить тестовый пример, мы создаем пользователя и задачу (обратите внимание, что из -за отношений с иностранным ключом между задачей и пользователем удаление пользователя в teardown () также удаляет задачу). Здесь мы видим, что любой метод испытаний может иметь несколько утверждений, и проходит только в случае успеха всех утверждений. Мы можем написать в базу данных вне функции настройки, что мы делаем при обновлении задачи. В противном случае, это очень похож на тест на регистрацию, на самом деле, большинство модельных тестовых случаев – это просто создание, чтение, обновление и уничтожение объектов в базе данных, хотя модели с методами более интересны для тестирования.

class SignInViewTest(TestCase):

    def setUp(self):
        self.user = get_user_model().objects.create_user(username='test',
                                                         password='12test12',
                                                         email='test@example.com')

    def tearDown(self):
        self.user.delete()

    def test_correct(self):
        response = self.client.post('/signin/', {'username': 'test', 'password': '12test12'})
        self.assertTrue(response.data['authenticated'])

    def test_wrong_username(self):
        response = self.client.post('/signin/', {'username': 'wrong', 'password': '12test12'})
        self.assertFalse(response.data['authenticated'])

    def test_wrong_pssword(self):
        response = self.client.post('/signin/', {'username': 'test', 'password': 'wrong'})
        self.assertFalse(response.data['authenticated'])

Просмотры тестирования несколько сложнее, чем тестирование моделей. Однако, когда мы пишем API, нам не нужно беспокоиться о тестировании передней части, как мы бы в веб -приложении. Таким образом, мы можем заменить большую часть нашего бывшего ручного тестирования через Postman тестами просмотров. Self.client является клиентом HTTP в библиотеке тестирования Django. Мы используем его, чтобы сделать запрос сообщения в «/signin/» с учетными данными пользователя. Мы проверяем то же самое, что и раньше: правильная информация о входе в систему, неправильное имя пользователя и неправильный пароль. Это особенно полезно, потому что это показывает нам что, если модель проходит, но тестирование просмотров проходит, проблема не с моделью, ограничивая объем нашей отладки. Мы делаем аналогичную вещь для взглядов, связанных с задачами.

class AllTasksViewTest(TestCase):

    def setUp(self):
        self.user = get_user_model().objects.create_user(username='test',
                                                         password='12test12',
                                                         email='test@example.com')
        self.user.save()
        self.timestamp = date.today()
        self.client.login(username='test', password='12test12')

    def tearDown(self):
        self.user.delete()

    def test_no_tasks(self):
        response = self.client.get('/all/')
        self.assertEqual(response.data, {'tasks': []})

    def test_one_task(self):
        self.task1 = Task(user=self.user,
                          description='description 1',
                          due=self.timestamp + timedelta(days=1))
        self.task1.save()
        response = self.client.get('/all/')
        self.assertEqual(response.data, {'tasks': [OrderedDict([('id', 1),
                                                                ('description', 'description 1'),
                                                                ('due', str(self.timestamp + timedelta(days=1)))])]})

Этот случай проверяет конечную точку “/all/”. Сам тестовый пример имеет больше методов, но скопированный выше фрагмент показывает нам все новые вещи. В настройке мы используем self.client.login () так что клиент действует как зарегистрированный пользователь. Затем мы создаем задачи и сравниваем их с отформатированным выводом, который мы ожидаем. Этот пример более четко иллюстрирует преимущества setup () и Teardown () Методы, поскольку задачи из одного теста не переносятся в другие. Опять же, этот тест изолирует компонент представления, поскольку базовая модель отдельно протестирована.

Теперь, когда вы понимаете тестовый код, запустите Python Manage.py Test запустить все тесты. Давайте рассмотрим вывод:

Creating test database for alias 'default'...
System check identified no issues (0 silenced).
...FF..........
======================================================================
FAIL: test_due_future (todo.tests.DueTodayTest)
---------------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/Philip/Code/WFH/mkdev_blog/djangotesting/taskmanager/todo/tests.py", line 155, in test_due_future
    self.assertFalse(self.task.due_today())
AssertionError: True is not false

======================================================================
FAIL: test_due_past (todo.tests.DueTodayTest)
---------------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/Philip/Code/WFH/mkdev_blog/djangotesting/taskmanager/todo/tests.py", line 161, in test_due_past
    self.assertFalse(self.task.due_today())
AssertionError: True is not false

---------------------------------------------------------------------------
Ran 15 tests in 2.232s

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

Все проходящие тесты указаны с Анкет В то время как провальные тесты получают F Анкет Неудачные тесты доходности, объясняющие, почему утверждения не удались. Тесты, о которых мы еще не говорили, проваливаются, которые мы исправим в следующем разделе. Прежде чем мы продолжим, вы можете заметить, что этот код тестирования невероятно многослов. Действительно, мы проверили лишь небольшой объем нашей функциональности, и все же файл тестов уже составляет столько строк кода, сколько файл представлений. Этого следовало ожидать, у вас действительно не может быть слишком много тестов, если все они точны. Когда вы измените свой код, некоторые тесты прорвутся, что просто показывает вам, какие тесты вам нужно исправить. Таким образом, вы должны рассчитывать на постоянное накопление кода тестирования и ожидать, что в среднем производственном приложении у вас будет в несколько раз больше строк тестов, чем строки кода.

Что такое тестовая разработка?

Позвольте мне на мгновение вернуться в начальную историю. После нескольких недель борьбы с ошибкой после ошибки мы с командой написали модульные тесты для всей кодовой базы. У нас было полное тестовое покрытие, что означает, что каждая строка кода была проверена хотя бы одним тестом. Это длилось пару недель, пока мы не решили существенно изменить нашу схему базы данных. Вместо того, чтобы переписывать тесты, мы отказались от сломанных, и через несколько дней мы снова начали испытывать ошибки «случайного поломки». Тестовая разработка предотвратила бы это обратное уплотнение.

Чтобы оставаться полезными, ваши тесты требуют обновления вместе с вашим кодом. Некоторые программисты практикуют разработку тестирования, чтобы оставаться впереди изменений кода. Когда вы разрабатываете функцию, первое, что вы делаете, это определите, что должна делать эта функция. Разработка, управляемая тестированием, формализует этот процесс, сначала написав тесты для этой функции. Общая идея заключается в том, что вы пишете один или несколько тестов, которые определяют эту функцию, код до прохода этих тестов, затем повторяйте, написав больше тестов.

Возвращение к неудачным тестам показывает нам, что нам нужно реализовать due_today () Метод в Задача модель. Изучая тесты, мы видим, что он должен вернуть Верно Если задача должна быть сегодня, иначе она должна вернуть Ложный Анкет Скопируйте приведенный ниже код, чтобы заменить существующее due_today () Метод в Задача Модель затем снова запустите тесты.

def due_today(self):
    return self.due == date.today()

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

К тест Вы понимаете, попробуйте написать тесты для других существующих представлений или использовать тесты для определения новых функций, а затем добавьте эти функции. Легким было бы логическое поле завершен в задача Модель, которая может быть установлена на истину после выполнения задачи, что позволяет нам сохранить выполненные задачи вместо того, чтобы удалить их. Затем посмотрите на добавление тестов в свои собственные проекты. Может быть пугающим смотреть на большой, непроверенный проект и попытаться довести его до полного покрытия. Вместо того, чтобы пытаться проверить все одновременно, добавьте тесты в небольшие куски проекта или новые функции, когда вы разрабатываете их и создаете для полного покрытия.

Дальнейшая ссылка:

Это Статья MKDEV написано Филиппом Кили. Ты могу Нанимайте наших наставников Python И изучите питон для себя.

Оригинал: “https://dev.to/mkdev/how-to-cover-django-application-with-unit-tests-4ei3”