Модульное тестирование в Python с Unittest

2 min


Введение

Практически во всех областях продукты тщательно тестируются перед выпуском на рынок, чтобы гарантировать их качество и работоспособность.

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

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

Модульное тестирование против других форм тестирования

Существуют различные способы тестирования программного обеспечения, которые в основном сгруппированы в функциональная а также нефункциональные тестирование.

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

Модульное тестирование подпадает под функциональное тестирование вместе с интеграционное тестирование а также регрессионное тестирование,

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

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

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

Преимущества модульного тестирования

Модульное тестирование выгодно тем, что оно исправляет ошибки и проблемы на ранних этапах процесса разработки и в конечном итоге ускоряет его.

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

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

Unittest Framework

Вдохновленный Среда тестирования JUnit для Java, unittest это среда тестирования для программ на Python, которая поставляется вместе с дистрибутивом Python начиная с Python 2.1. Иногда его называют PyUnit, Инфраструктура поддерживает автоматизацию и объединение тестов, а также общий код настройки и завершения их работы.

Это достигается благодаря многим следующим концепциям:

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

unittest это не единственная среда тестирования для Python, другие включают Pytest, Robot Framework, Салат для BDD и Behave Framework,

Если вам интересно больше узнать о Тестовая разработка в Python с PyTestМы вас покроем!

Unittest Framework в действии

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

Хотя это хорошая практика для разработки нашего приложения Python в виртуальной среде, для этого примера это не будет обязательным, поскольку unittest поставляется с дистрибутивом Python, и нам не понадобятся никакие другие внешние пакеты для сборки нашего калькулятора.

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

Мы протестируем четыре операции, поддерживаемые нашим калькулятором, отдельно и запишем тесты для каждой в отдельном наборе тестов, поскольку ожидается, что тесты конкретной операции будут выполняться вместе. Наши тестовые наборы будут размещены в одном файле, а наш калькулятор – в отдельном файле.

Наш калькулятор будет SimpleCalculator класс с функциями для обработки четырех ожидаемых от него операций. Давайте начнем тестирование, написав тесты для операции сложения в нашем test_simple_calculator.py:

import unittest
from simple_calculator import SimpleCalculator

class AdditionTestSuite(unittest.TestCase):
    def setUp(self):
        """ Executed before every test case """
        self.calculator = SimpleCalculator()

    def tearDown(self):
        """ Executed after every test case """
        print("ntearDown executing after the test case. Result:")

    def test_addition_two_integers(self):
        result = self.calculator.sum(5, 6)
        self.assertEqual(result, 11)

    def test_addition_integer_string(self):
        result = self.calculator.sum(5, "6")
        self.assertEqual(result, "ERROR")

    def test_addition_negative_integers(self):
        result = self.calculator.sum(-5, -6)
        self.assertEqual(result, -11)
        self.assertNotEqual(result, 11)

# Execute all the tests when the file is executed
if __name__ == "__main__":
    unittest.main()

Начнем с импорта unittest модуль и создание набора тестов (AdditionTestSuite) для операции сложения.

В нем мы создаем setUp() метод, который называется до каждый тестовый случай, чтобы создать наш SimpleCalculator объект, который будет использоваться для выполнения расчетов.

tearDown() метод выполнен после каждый тестовый случай, и так как в данный момент мы не особо используем его, мы просто используем его для распечатки результатов каждого теста.

Функции test_addition_two_integers(), test_addition_integer_string() а также test_addition_negative_integers() наши тесты Ожидается, что калькулятор добавит два положительных или отрицательных целых числа и вернет сумму. Когда мы представляем целое число и строку, наш калькулятор должен возвращать ошибку.

assertEqual() а также assertNotEqual() это функции, которые используются для проверки результатов нашего калькулятора. assertEqual() Функция проверяет, равны ли два предоставленных значения, в нашем случае мы ожидаем сумму 5 а также 6 быть 11, поэтому мы сравним это со значением, возвращаемым нашим калькулятором.

Если два значения равны, тест пройден. Другие функции утверждения, предлагаемые unittest включают:

  • assertTrue(a): Проверяет, является ли предоставленное выражение true
  • assertGreater(a, b): Проверяет ли a больше, чем b
  • assertNotIn(a, b): Проверяет ли a в b
  • assertLessEqual(a, b): Проверяет ли a меньше или равно b
  • так далее…

Список этих утверждений можно найти в этот шпаргалка,

Когда мы выполняем тестовый файл, это вывод:

$ python3 test_simple_calulator.py

tearDown executing after the test case. Result:
E
tearDown executing after the test case. Result:
E
tearDown executing after the test case. Result:
E
======================================================================
ERROR: test_addition_integer_string (__main__.AdditionTestSuite)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_simple_calulator.py", line 22, in test_addition_integer_string
    result = self.calculator.sum(5, "6")
AttributeError: 'SimpleCalculator' object has no attribute 'sum'

======================================================================
ERROR: test_addition_negative_integers (__main__.AdditionTestSuite)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_simple_calulator.py", line 26, in test_addition_negative_integers
    result = self.calculator.sum(-5, -6)
AttributeError: 'SimpleCalculator' object has no attribute 'sum'

======================================================================
ERROR: test_addition_two_integers (__main__.AdditionTestSuite)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_simple_calulator.py", line 18, in test_addition_two_integers
    result = self.calculator.sum(5, 6)
AttributeError: 'SimpleCalculator' object has no attribute 'sum'

----------------------------------------------------------------------
Ran 3 tests in 0.001s

FAILED (errors=3)

В верхней части вывода мы можем увидеть выполнение tearDown() Функция через печать сообщения, которое мы указали. Далее следует письмо E и сообщения об ошибках, возникающих при выполнении наших тестов.

Есть три возможных результата теста: он может пройти, провалиться или столкнуться с ошибкой. unittest Framework указывает три сценария с помощью:

  • Полный стоп (.): Указывает на прохождение теста
  • Буква "F": Указывает на провал теста
  • Буква "Е": Указывает на ошибку, возникшую во время выполнения теста

В нашем случае мы видим письмо EЭто означает, что наши тесты столкнулись с ошибками, возникшими при выполнении наших тестов. Мы получаем ошибки, потому что мы еще не реализовали addition Функциональность нашего калькулятора:

class SimpleCalculator:
    def sum(self, a, b):
        """ Function to add two integers """
        return a + b

Наш калькулятор теперь готов добавить два числа, но чтобы быть уверенным, что он будет работать так, как ожидалось, давайте удалим tearDown() функционировать из наших тестов и запустить наши тесты еще раз:

$ python3 test_simple_calulator.py
E..
======================================================================
ERROR: test_addition_integer_string (__main__.AdditionTestSuite)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_simple_calulator.py", line 22, in test_addition_integer_string
    result = self.calculator.sum(5, "6")
  File "/Users/robley/Desktop/code/python/unittest_demo/src/simple_calculator.py", line 7, in sum
    return a + b
TypeError: unsupported operand type(s) for +: 'int' and 'str'

----------------------------------------------------------------------
Ran 3 tests in 0.002s

FAILED (errors=1)

Наши ошибки сократились с 3 до 1 раз. Резюме отчета в первой строке E.. указывает, что один тест привел к ошибке и не смог завершить выполнение, а остальные два пройдены. Чтобы выполнить первый тестовый тест, мы должны реорганизовать нашу функцию суммы следующим образом:

    def sum(self, a, b):
        if isinstance(a, int) and isinstance(b, int):
            return a + b

Когда мы запустим наши тесты еще раз:

$ python3 test_simple_calulator.py
F..
======================================================================
FAIL: test_addition_integer_string (__main__.AdditionTestSuite)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_simple_calulator.py", line 23, in test_addition_integer_string
    self.assertEqual(result, "ERROR")
AssertionError: None != 'ERROR'

----------------------------------------------------------------------
Ran 3 tests in 0.001s

FAILED (failures=1)

На этот раз наша функция sum выполняется до завершения, но наш тест не пройден. Это потому, что мы не возвращали никакого значения, когда один из входных данных не является целым числом. Наше утверждение сравнивает None в ERROR и поскольку они не равны, тест не пройден. Чтобы пройти тест, мы должны вернуть ошибку в нашем sum() функция:

def sum(self, a, b):
    if isinstance(a, int) and isinstance(b, int):
        return a + b
    else:
        return "ERROR"

И когда мы запускаем наши тесты:

$ python3 test_simple_calulator.py
...
----------------------------------------------------------------------
Ran 3 tests in 0.000s

OK

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

Мы также можем проверить, возникло ли исключение. Например, когда число делится на ноль, ZeroDivisionError исключение поднято. В нашем DivisionTestSuiteМы можем подтвердить, было ли возбуждено исключение:

class DivisionTestSuite(unittest.TestCase):
    def setUp(self):
        """ Executed before every test case """
        self.calculator = SimpleCalculator()

    def test_divide_by_zero_exception(self):
        with self.assertRaises(ZeroDivisionError):
            self.calculator.divide(10, 0)

test_divide_by_zero_exception() выполнит divide(10, 0) Функция нашего калькулятора и подтвердит, что исключение действительно было поднято. Мы можем выполнить DivisionTestSuite в отдельности, следующим образом:

$ python3 -m unittest test_simple_calulator.DivisionTestSuite.test_divide_by_zero_exception
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

Полный набор тестов функциональности деления можно найти в приведенной ниже сущности вместе с наборами тестов для функций умножения и вычитания.

Вывод

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

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

Полный калькулятор и наборы тестов можно найти здесь в этом Gist на GitHub,


0 Comments

Ваш e-mail не будет опубликован. Обязательные поля помечены *