Добрый день.
Модуль unittest входит в стандартную библиотеку Python и служит базовым инструментом для организации регрессионных unit-тестов. Рассмотрим небольшой пример. Файл account.py содержит класс BankAccount, предоставляющий средства для работы с банковским счетом: создание счета с начислением бонуса, добавление и снятие денег, начисление процентов.
class BankAccount: def __init__ (self, bonus): self.balance = bonus def deposit (self, amount): self.balance += amount def withdraw (self, amount): if self.balance > amount: self.balance -= amount else: self.balance = 0 def interest (self, rate): self.balance = self.balance + (self.balance * rate)/100 def get(self): return self.balance if __name__ == '__main__': account = BankAccount(30) account.deposit(50) account.withdraw(10) account.interest(8.5) balance = account.get() print balance
Запуск account.py из командной строки cлужит примером работы с BankAccount (результат = 75.95). Подготовим тесты для трех методов: deposit, withdraw и interest. Разместим их в модуле test_account.py:
from account import BankAccount import unittest class TestBankAccount (unittest.TestCase): def setUp(self): self.account = BankAccount (100) def testBankAccountDeposit(self): test_balance = 170 self.account.deposit (70) self.assertEqual(self.account.balance, test_balance) def testBankAccountWithdraw(self): test_balance = 30 self.account.withdraw (70) self.assertEqual(self.account.balance, test_balance) self.account.withdraw (270) self.assertEqual(self.account.balance, 0) def testBankAccountInterest(self): test_balance = 108.5 self.account.interest (8.5) self.assertEqual(self.account.balance, test_balance) if __name__ == "__main__": unittest.main()
Метод setUp() – служебный. Он вызывается перед запуском каждого теста и подготавливает среду выполнения. В нашем случае метод setUp() создает банковский аккаунт и помещает на счет 100 единиц. Имена остальных методов начинаются с “test” (необходимое условие для нахождения тестов в коде модуля). Запуск test_account.py:
[capt@rh Work]# python test_account.py -v testBankAccountDeposit (__main__.TestBankAccount) ... ok testBankAccountInterest (__main__.TestBankAccount) ... ok testBankAccountWithdraw (__main__.TestBankAccount) ... ok ---------------------------------------------------------------------- Ran 3 tests in 0.005s OK
Приложение, достойное модульного тестирования, как правило содержит больше одного модуля. Соответственно, unittest предоставляет возможности для централизованного управления всеми тестами. Создадим агрегатор, который будет запускать тесты из класса TestBankAccount и тесты нахождения палиндромов.
Прежде всего, внесем изменения в test_account.py, добавив в него создание комплекта тестов класса BankAccount:
from account import BankAccount import unittest class TestBankAccount (unittest.TestCase): def setUp(self): self.account = BankAccount (100) def testBankAccountDeposit(self): test_balance = 170 self.account.deposit (70) self.assertEqual(self.account.balance, test_balance) def testBankAccountWithdraw(self): test_balance = 30 self.account.withdraw (70) self.assertEqual(self.account.balance, test_balance) self.account.withdraw (270) self.assertEqual(self.account.balance, 0) def testBankAccountInterest(self): test_balance = 108.5 self.account.interest (8.5) self.assertEqual(self.account.balance, test_balance) def suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(TestBankAccount)) return suite
Далее, подготовим служебный файл test_transformation с результатами doctest-проверки наличия палиндромов:
>>> from transformation import is_palindrome >>> is_palindrome("hello hhh again") hhh >>> is_palindrome("") Traceback (most recent call last): ... ValueError: Empty String!
Создадим скрипт-агрегатор test_aggregator.py:
import unittest import test_account import doctest import transformation suiteAccount = test_account.suite() suitePalindrome = unittest.TestSuite() suitePalindrome.addTest(doctest.DocFileSuite("test_transformation")) suite = unittest.TestSuite() suite.addTest(suiteAccount) suite.addTest(suitePalindrome) unittest.TextTestRunner(verbosity=2).run(suite)
Запуск test_aggregator.py:
[capt@rh Work]# python test_aggregator.py testBankAccountDeposit (test_account.TestBankAccount) ... ok testBankAccountInterest (test_account.TestBankAccount) ... ok testBankAccountWithdraw (test_account.TestBankAccount) ... ok Doctest: test_transformation ... ok ---------------------------------------------------------------------- Ran 4 tests in 0.005s OK
Убедимся, что при изменении алгоритма метода withdraw наш тест закончится неудачей. Изменим этот метод в модуле account.py:
def withdraw (self, amount): self.balance -= amount
Запуск test_aggregator.py:
[capt@rh Work]# python test_aggregator.py testBankAccountDeposit (test_account.TestBankAccount) ... ok testBankAccountInterest (test_account.TestBankAccount) ... ok testBankAccountWithdraw (test_account.TestBankAccount) ... FAIL Doctest: test_transformation ... ok ====================================================================== FAIL: testBankAccountWithdraw (test_account.TestBankAccount) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/Work/test_account.py", line 19, in testBankAccountWithdraw self.assertEqual(self.account.balance, 0) AssertionError: -240 != 0 ---------------------------------------------------------------------- Ran 4 tests in 0.006s FAILED (failures=1)
Результат соответствует ожидаемому.
Дополнительную информацию о модуле unittest можно почерпнуть в документации Python.
Успехов в модульных тестах. Оставайтесь с нами.