Знакомство с инструментами тестирования в Python
По определению, тестовое приспособление (фикстура) — это функция или метод, который запускается в Python до и после выполнения блока тестового кода. Другими словами, фикстуры используются в тестировании как этап, выполняемый до или после теста.
Module-level фикстуры
Предположим, у вас есть тестовый модуль под названием test_my_module.py. В test_my_module.py функции setUpModule() и TearDownModule() являются class-level фикстурами.
- Функция setUpModule() выполняется перед всеми методами тестирования в тестовом модуле.
- Функция TearDownModule() запускается после всех методов тестового модуля.
См. следующий пример:
import unittest
def setUpModule():
print('Running setUpModule')
def tearDownModule():
print('Running tearDownModule')
class TestMyModule(unittest.TestCase):
def test_case_1(self):
self.assertEqual(5+5, 10)
def test_case_2(self):
self.assertEqual(1+1, 2)
Если вы запустите тест:
python -m unittest -v
Выход:
Running setUpModule test_case_1(test_my_module.TestMyModule) ... ok test_case_2(test_my_module.TestMyModule) ... ok Running tearDownModule ---------------------------------------------------------------------- Ran 2 tests in 0.001s OK
В этом примере функция setUpModule() запускается перед всеми методами тестирования, а функция TearDownModule() запускается после всех методов тестирования.
Class-level фикстуры
setUpClass() и TearDownClass() являются class-level фикстурами:
- SetUpClass() запускается перед всеми тестовыми методами класса.
- TearDownClass() запускается после всех тестовых методов класса.
Например:
import unittest
def setUpModule():
print('Running setUpModule')
def tearDownModule():
print('Running tearDownModule')
class TestMyModule(unittest.TestCase):
@classmethod
def setUpClass(cls):
print('Running setUpClass')
@classmethod
def tearDownClass(cls):
print('Running tearDownClass')
def test_case_1(self):
self.assertEqual(5+5, 10)
def test_case_2(self):
self.assertEqual(1+1, 2)
В этом примере мы добавили методы класса: setUpClass() и TearDownClass() в класс TestMyModule.
Если вы запустите тест, вы увидите следующий результат:
Running setUpModule Running setUpClass test_case_1(test_my_module.TestMyModule) ... ok test_case_2(test_my_module.TestMyModule) ... ok Running tearDownClass Running tearDownModule ---------------------------------------------------------------------- Ran 2 tests in 0.001s OK
Фикстуры method-level
setUp() и TearDown() являются method-level фикстурами:
- Функция setUp() выполняется перед каждым тестовым методом в тестовом классе.
- TearDown() запускается после каждого тестового метода в тестовом классе.
Например:
import unittest
def setUpModule():
print('Running setUpModule')
def tearDownModule():
print('Running tearDownModule')
class TestMyModule(unittest.TestCase):
@classmethod
def setUpClass(cls):
print('Running setUpClass')
@classmethod
def tearDownClass(cls):
print('Running tearDownClass')
def setUp(self):
print('')
print('Running setUp')
def tearDown(self):
print('Running tearDown')
def test_case_1(self):
print('Running test_case_1')
self.assertEqual(5+5, 10)
def test_case_2(self):
print('Running test_case_2')
self.assertEqual(1+1, 2)
Ниже показаны результаты теста:
Running setUpModule Running setUpClass test_case_1(test_my_module.TestMyModule) ... Running setUp Running test_case_1 Running tearDown ok test_case_2(test_my_module.TestMyModule) ... Running setUp Running test_case_2 Running tearDown ok Running tearDownClass Running tearDownModule ---------------------------------------------------------------------- Ran 2 tests in 0.002s OK
В этом примере методы setUp() и TearDown() выполняются до и после каждого метода тестирования, включая test_case_1() и test_case_2().
Пример использования тестовых фикстур в Python
Сначала определите классы BankAccount и InsufficientFund в модуле bank_account.py:
class InsufficientFund(Exception):
pass
class BankAccount:
def __init__(self, balance: float) -> None:
if balance < 0:
raise ValueError('balance cannot be negative')
self._balance = balance
@property
def balance(self) -> float:
return self._balance
def deposit(self, amount: float) -> None:
if amount <= 0:
raise ValueError('The amount must be positive')
self._balance += amount
def withdraw(self, amount: float) -> None:
if amount <= 0:
raise ValueError('The withdrawal amount must be more than 0')
if amount > self._balance:
raise InsufficientFund('Insufficient ammount for withdrawal')
self._balance -= amount
Во-вторых, определите класс TestBankAccount в модуле test_bank_account.py:
import unittest
from bank_account import BankAccount
class TestBankAccount(unittest.TestCase):
def test_deposit(self):
self.bank_account = BankAccount(100)
self.bank_account.deposit(100)
self.assertEqual(self.bank_account.balance, 200)
def test_withdraw(self):
self.bank_account = BankAccount(100)
self.bank_account.withdraw(50)
self.assertEqual(self.bank_account.balance, 50)
Класс TestBankAccount имеет два метода тестирования:
- test_deposit() – проверить метод депозита() банковского счета.
- test_withdraw() – протестируйте метод вывода() банковского счета.
Оба метода создают новый экземпляр BankAccount. Это излишне.
Чтобы избежать избыточности, вы можете создать экземпляр класса BankAccount в методе setUp() и использовать его во всех методах тестирования:
import unittest
from bank_account import BankAccount
class TestBankAccount(unittest.TestCase):
def setUp(self) -> None:
self.bank_account = BankAccount(100)
def test_deposit(self):
self.bank_account.deposit(100)
self.assertEqual(self.bank_account.balance, 200)
def test_withdraw(self):
self.bank_account.withdraw(50)
self.assertEqual(self.bank_account.balance, 50)
В методе setUp():
- Сначала создайте экземпляр класса BankAccount и присвойте его переменной экземпляра self.bank_account.
- Затем используйте экземпляр self.bank_account в методах test_deposit() и test_withdraw().
При запуске методов тестирования test_deposit() и test_withdraw() метод setUp() запускается перед каждым методом тестирования.
Для метода test_deposit():
setUp() test_deposit()
Для метода test_withdraw():
setUp() test_withdraw()
Если вы запустите тест:
python -m unittest -v
Он выведет следующее:
test_deposit(test_bank_account.TestBankAccount) ... ok test_withdraw(test_bank_account.TestBankAccount) ... ok ---------------------------------------------------------------------- Ran 2 tests in 0.001s OK
Следующий код добавляет метод TearDown() в TestBankAccount:
import unittest
from bank_account import BankAccount, InsufficientFund
class TestBankAccount(unittest.TestCase):
def setUp(self) -> None:
self.bank_account = BankAccount(100)
def test_deposit(self):
self.bank_account.deposit(100)
self.assertEqual(self.bank_account.balance, 200)
def test_withdraw(self):
self.bank_account.withdraw(50)
self.assertEqual(self.bank_account.balance, 50)
def tearDown(self) -> None:
self.bank_account = None
Метод TearDown() присваивает None экземпляру self.bank_account.
