Рассмотрим, как определять параметризованные тесты с помощью контекстного менеджера subTest() Unittest в Python.
Знакомство с контекстным менеджером subTest
- Сначала создайте новый модуль с именем «pricing.py» и определите функцию Calculation() следующим образом:
def calculate(price, tax=0, discount=0):
return round((price - discount) *(1+tax), 2)
Функция Calculation() вычисляет чистую цену на основе цены, налога и скидки.
- Во-вторых, создайте тестовый модуль test_pricing.py для проверки функции Calculation():
import unittest
from pricing import calculate
class TestPricing(unittest.TestCase):
def test_calculate(self):
pass
Чтобы протестировать функцию Calculation(), вам нужно придумать несколько тестовых примеров, например:
- Имеет цену без налога и скидки.
- Цена указана с налогом, но без скидки.
- Имеется цена без налога, но со скидкой.
- Цена с налогом и скидкой.
Чтобы охватить эти случаи, вам необходимо иметь различные методы тестирования. Или вы можете определить один метод тестирования и предоставить данные тестирования из списка случаев. Например:
import unittest
from pricing import calculate
class TestPricing(unittest.TestCase):
def test_calculate(self):
items =(
{'case': 'No tax, no discount', 'price': 10, 'tax': 0, 'discount': 0, 'net_price': 10},
{'case': 'Has tax, no discount', 'price': 10, 'tax': 0.1, 'discount': 0, 'net_price': 10},
{'case': 'No tax, has discount', 'price': 10, 'tax': 0, 'discount': 1, 'net_price': 10},
{'case': 'Has tax, has discount', 'price': 10, 'tax': 0.1, 'discount': 1, 'net_price': 9.9},
)
for item in items:
with self.subTest(item['case']):
net_price = calculate(
item['price'],
item['tax'],
item['discount']
)
self.assertEqual(
net_price,
item['net_price']
)
Запустите тест:
python -m unittest test_pricing -v
Выход:
test_calculate(test_pricing.TestPricing) ... FAIL
======================================================================
FAIL: test_calculate(test_pricing.TestPricing)
----------------------------------------------------------------------
Traceback(most recent call last):
File "D:\python-unit-testing\test_pricing.py", line 26, in test_calculate
self.assertEqual(
AssertionError: 11.0 != 10
----------------------------------------------------------------------
Ran 1 test in 0.002s
FAILED(failures=1)
Проблема этого подхода в том, что тест останавливается после первой неудачи. Чтобы решить эту проблему, unittest предоставляет вам контекстный менеджер subTest(). Например:
import unittest
from pricing import calculate
class TestPricing(unittest.TestCase):
def test_calculate(self):
items =(
{'case': 'No tax, no discount', 'price': 10, 'tax': 0, 'discount': 0, 'net_price': 10},
{'case': 'Has tax, no discount', 'price': 10, 'tax': 0.1, 'discount': 0, 'net_price': 10},
{'case': 'No tax, has discount', 'price': 10, 'tax': 0, 'discount': 1, 'net_price': 10},
{'case': 'Has tax, has discount', 'price': 10, 'tax': 0.1, 'discount': 1, 'net_price': 9.9},
)
for item in items:
with self.subTest(item['case']):
net_price = calculate(
item['price'],
item['tax'],
item['discount']
)
self.assertEqual(
net_price,
item['net_price']
)
Запустите тест:
python -m unittest test_pricing -v
Выход:
test_calculate(test_pricing.TestPricing) ...
======================================================================
FAIL: test_calculate(test_pricing.TestPricing) [Has tax, no discount]
----------------------------------------------------------------------
Traceback(most recent call last):
File "D:\python-unit-testing\test_pricing.py", line 26, in test_calculate
self.assertEqual(
AssertionError: 11.0 != 10
======================================================================
FAIL: test_calculate(test_pricing.TestPricing) [No tax, has discount]
----------------------------------------------------------------------
Traceback(most recent call last):
File "D:\python-unit-testing\test_pricing.py", line 26, in test_calculate
self.assertEqual(
AssertionError: 9 != 10
----------------------------------------------------------------------
Ran 1 test in 0.001s
FAILED(failures=2)
Благодаря использованию контекстного менеджера subTest() тест не остановился после первого сбоя. Кроме того, после каждого сбоя отображается очень подробное сообщение, чтобы вы могли изучить ситуацию.
Синтаксис контекстного менеджера subTest()
Ниже показан синтаксис контекстного менеджера subTest():
def subTest(self, msg=_subtest_msg_sentinel, **params):
SubTest() возвращает менеджер контекста. Необязательный параметр msg идентифицирует закрытый блок подтеста, возвращенный менеджером контекста.
Если произойдет сбой, тестовый пример будет помечен как неудавшийся. Однако он возобновляет выполнение в конце вложенного блока, позволяя выполнить дальнейший тестовый код.
