OpenQuality.ru

Качество программного обеспечения

Качество программного обеспечения: в главных ролях

Лента  Радар  Блог  Опыт  
Разум  Видео  Заметки  Эпизоды


Python unittest: базовые возможности

Добрый день.

Модуль 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.

Успехов в модульных тестах. Оставайтесь с нами.

Отправить в Twitter, Facebook, FriendFeed, ВКонтакте | Опубликовано 02.02.2009 в рубрике "Модульные тесты"

Комментарии (8)

  1. Pingback : OpenQuality.ru | Ступень Мартина, или двойники в Python | April 21, 2009

    […] возьмем файл account.py, содержащий класс BankAccount, из нашего примера c unittest. Далее создадим файл test.py, в котором […]


  2. Автор комментария : Ochir | June 23, 2009

    Немного из своего опыта.
    Очень удобна структура построения тестового фреймворка на базе чистого unittest, если необходимо иметь четкую структуру тестов, основанных на базовом классе.

    Но гораздо удобнее использовать nosetests (надстройка для unitttest) или py.test (часть библиотеки py.lib). Оба обладают схожими возможностями и позволяет эффективно управлять тестами.

    [Ответить]


  3. Автор комментария : Капитан Аляска | June 23, 2009

    Ochir, спасибо за ценный комментарий! Да, в сторону nose я смотрел, но пока функциональности unittest хватало. py.test – пока нет. Много их, framework’ов :)

    [Ответить]


  4. Автор комментария : Ochir | June 24, 2009

    а чего в nose не хватает?
    это же надстройка вроде, он все возможности unittest в себя включает и можно писать обычные unittest, но потом отлаживать с помощью nosetests с опциями.

    Или если unittest совсем не нравится, можно сразу py.test пробовать.

    [Ответить]


  5. Автор комментария : Капитан Аляска | June 24, 2009

    Я сформулировал это для себя несколько иначе: не увидел в nose чего-нибудь такого, чтобы захотелось его использовать. Функциональности unittest для моих задач было достаточно. А дальше – время покажет.

    [Ответить]


  6. Автор комментария : Ochir | June 24, 2009

    согласен, это дело удобства и привычки.

    [Ответить]


  7. Автор комментария : wessenhizer | May 13, 2011

    Статья понравилась. Я до этого читал другие статьи и не мог понять что за тесты такие и как их делать, а тут обратил внимание, что меня привлекло в этой статье, оказалось подсветка синтаксиса! Я был удивлён своему открытию, я просто читал код и не замечал подсветку, и код из за этого выглядел понятнее. Питонер я начинающий так что возьму на заметку.

    [Ответить]


  8. Автор комментария : Капитан | May 23, 2011

    Wessenhizer, спасибо на добром слове.

    [Ответить]



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

Пожалуйста, исправьте результат: дважды два равно



КРАТКОЕ СОДЕРЖАНИЕ

Что такое качество программного обеспечения и как его улучшить: теория и практика, задачи и решения, подводные камни и обходные пути.


ПУТЕВОДИТЕЛЬ

Список всех статей с краткой аннотацией и разбивкой по рубрикам. Открыть карту.

ПОДПИСКА

Доступ к самым интересным материалам по электропочте и RSS. Подробности.

ИЩЕЙКА