Юнит-тесты на Python: Быстрый старт

от автора

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


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

В этой статье я продемонстрирую основную идею юнит-тестирования на одном классе. На практике вам придется писать множество тестовых случаев, добавлять их в тестовый набор и запускать все вместе. Управление тест-кейсами мы рассмотрим в следующей статье.

Сегодня мы сосредоточимся на тестировании бэкенда. То есть разработчик реализовал некоторый проект согласно спецификациям (например, 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 тестов прошли успешно, а один не удался. В реальном сценарии, предполагается, что ваш тестовый случай является верным, то есть таким образом он помогает определять неправильно реализованную функцию.


ссылка на оригинал статьи https://habr.com/ru/company/otus/blog/481806/


Комментарии

Добавить комментарий

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