Перевод статьи подготовлен специально для студентов курса «Python QA Engineer».

Юнит-тестирование кода является неотъемлемой частью жизненного цикла разработки программного обеспечения. Юнит-тесты также формируют основу для проведения регрессионного тестирования, то есть они гарантируют, что система будет вести себя согласно сценарию, когда добавятся новые функциональные возможности или изменятся существующие.
В этой статье я продемонстрирую основную идею юнит-тестирования на одном классе. На практике вам придется писать множество тестовых случаев, добавлять их в тестовый набор и запускать все вместе. Управление тест-кейсами мы рассмотрим в следующей статье.
Сегодня мы сосредоточимся на тестировании бэкенда. То есть разработчик реализовал некоторый проект согласно спецификациям (например, Calculator.py), а ваша задача состоит в том, чтобы убедиться, что разработанный код действительно им соответствует (например, с помощью
Предположим, что вы написали класс Calculator для выполнения основных вычислительных функций: сложения, вычитания, умножения и деления.
Код для этого здесь (
Теперь я хочу запустить юнит-тест, чтобы понять, что функциональность в приведенном выше классе работает, как запланировано.
Обычно Python поставляется уже с пакетом
Юнит-тест имеет следующую структуру:

Пришло время посмотреть на код тест-кейса. Вот файл
Хотя это и не обязательно, но как правило я называю тестовый класс с префиксом Test (в нашем случае TestCalculator). Ключевым требованием в этом классе является наличие надкласса
Всякий раз, когда выполняется этот тест-кейс, сначала выполняется метод setUp(). В нашем случае мы просто создаем объект класса Calculator и сохраняем его как атрибут класса. В родительском классе есть несколько других методов по умолчанию, которые мы рассмотрим позже.
На данный момент все, что вы будете делать, это писать методы
В каждом из методов тестирования я использовал встроенный метод
Есть множество встроенных методов
Последняя строка в приведенном выше коде просто запускает тестовый случай
Вы увидите вывод, сходный со следующим:
Что делать, если что-то не работает, как ожидалось? Давайте изменим ожидаемое значение
При запуске этого тест-кейса, вы получите следующий результат:
Здесь написано, что 3 из 4 тестов прошли успешно, а один не удался. В реальном сценарии, предполагается, что ваш тестовый случай является верным, то есть таким образом он помогает определять неправильно реализованную функцию.

Юнит-тестирование кода является неотъемлемой частью жизненного цикла разработки программного обеспечения. Юнит-тесты также формируют основу для проведения регрессионного тестирования, то есть они гарантируют, что система будет вести себя согласно сценарию, когда добавятся новые функциональные возможности или изменятся существующие.
В этой статье я продемонстрирую основную идею юнит-тестирования на одном классе. На практике вам придется писать множество тестовых случаев, добавлять их в тестовый набор и запускать все вместе. Управление тест-кейсами мы рассмотрим в следующей статье.
Сегодня мы сосредоточимся на тестировании бэкенда. То есть разработчик реализовал некоторый проект согласно спецификациям (например, Calculator.py), а ваша задача состоит в том, чтобы убедиться, что разработанный код действительно им соответствует (например, с помощью
TestCalculator.py
).Предположим, что вы написали класс Calculator для выполнения основных вычислительных функций: сложения, вычитания, умножения и деления.
Код для этого здесь (
Calculator.py
):#A simple calculator
class Calculator:
#empty constructor
def __init__(self):
pass
#add method - given two numbers, return the addition
def add(self, x1, x2):
return x1 + x2
#multiply method - given two numbers, return the
#multiplication of the two
def multiply(self, x1, x2):
return x1 * x2
#subtract method - given two numbers, return the value
#of first value minus the second
def subtract(self, x1, x2):
return x1 - x2
#divide method - given two numbers, return the value
#of first value divided by the second
def divide(self, x1, x2):
if x2 != 0:
return x1/x2
Теперь я хочу запустить юнит-тест, чтобы понять, что функциональность в приведенном выше классе работает, как запланировано.
Обычно Python поставляется уже с пакетом
unittest
. Если в вашей системе его нет, используйте pip для его установки. Юнит-тест имеет следующую структуру:

setUp()
и tearDown()
– это стандартные методы, которые поставляются с фреймворком unittest (они определены в классе unittest.TestCase). В зависимости от вашего тестового случая вы можете переопределять или не переопределять два этих метода по умолчанию. Пришло время посмотреть на код тест-кейса. Вот файл
TestCalculator.py
.import unittest
from Calculator import Calculator
#Test cases to test Calulator methods
#You always create a child class derived from unittest.TestCase
class TestCalculator(unittest.TestCase):
#setUp method is overridden from the parent class TestCase
def setUp(self):
self.calculator = Calculator()
#Each test method starts with the keyword test_
def test_add(self):
self.assertEqual(self.calculator.add(4,7), 11)
def test_subtract(self):
self.assertEqual(self.calculator.subtract(10,5), 5)
def test_multiply(self):
self.assertEqual(self.calculator.multiply(3,7), 21)
def test_divide(self):
self.assertEqual(self.calculator.divide(10,2), 5)
# Executing the tests in the above test case class
if __name__ == "__main__":
unittest.main()
Хотя это и не обязательно, но как правило я называю тестовый класс с префиксом Test (в нашем случае TestCalculator). Ключевым требованием в этом классе является наличие надкласса
unittest.TestCase
.Всякий раз, когда выполняется этот тест-кейс, сначала выполняется метод setUp(). В нашем случае мы просто создаем объект класса Calculator и сохраняем его как атрибут класса. В родительском классе есть несколько других методов по умолчанию, которые мы рассмотрим позже.
На данный момент все, что вы будете делать, это писать методы
test_xxx
для тестирования каждого метода в классе Calculator. Обратите внимание, что все тестовые методы начинаются с префикса test_
. Это говорит Python с помощью фреймворка unittest, что это методы тестирования. В каждом из методов тестирования я использовал встроенный метод
assertEqual
, чтобы проверить, возвращают ли методы калькулятора ожидаемое значение. Если возвращаемое значение равно ожидаемому значению, то тест проходит успешно, в противном случае он завершается неудачей.Есть множество встроенных методов
assert
, о которых мы будем говорить позже. Последняя строка в приведенном выше коде просто запускает тестовый случай
TestCalculator
. Он выполняет каждый тестовый метод, определенный внутри класса, и выдает результат.python TestCalculator.py -v
Вы увидите вывод, сходный со следующим:
test_add (__main__.TestCalculator) ... ok
test_divide (__main__.TestCalculator) ... ok
test_multiply (__main__.TestCalculator) ... ok
test_subtract (__main__.TestCalculator) ... ok
--------------------------------------------------------------------
Ran 4 tests in 0.000s
OK
Что делать, если что-то не работает, как ожидалось? Давайте изменим ожидаемое значение
test_divide
с 5 на 6 (5 – правильное значение, сейчас мы посмотрим, что случится при сбое. Это не ошибка в исходном коде, а ошибка в тестовом наборе, у вас тоже могут быть ошибки в тестовых наборах, поэтому всегда проверяйте тестовые сценарии на наличие ошибок!)import unittest
from Calculator import Calculator
#Test cases to test Calulator methods
#You always create a child class derived from unittest.TestCase class
class TestCalculator(unittest.TestCase):
#setUp method overridden from the parent class TestCase
def setUp(self):
self.calculator = Calculator()
...
def test_divide(self):
self.assertEqual(self.calculator.divide(10,2), 6)
# Executing the tests in the above test case class
if __name__ == "__main__":
unittest.main()
При запуске этого тест-кейса, вы получите следующий результат:
test_add (__main__.TestCalculator) ... ok
test_divide (__main__.TestCalculator) ... FAIL
test_multiply (__main__.TestCalculator) ... ok
test_subtract (__main__.TestCalculator) ... ok
====================================================================
FAIL: test_divide (__main__.TestCalculator)
--------------------------------------------------------------------
Traceback (most recent call last):
File "TestCalculator.py", line 23, in test_divide
self.assertEqual(self.calculator.divide(10,2), 6)
AssertionError: 5.0 != 6
--------------------------------------------------------------------
Ran 4 tests in 0.001s
FAILED (failures=1)
Здесь написано, что 3 из 4 тестов прошли успешно, а один не удался. В реальном сценарии, предполагается, что ваш тестовый случай является верным, то есть таким образом он помогает определять неправильно реализованную функцию.