Если возможно, все в нашем кодовой базе, каждая функция. Но это зависит как выбор разработчиков. Вы можете пропустить это, если не практично написать надежный тест. Как сказал Ник Коглан в гостевой сессии – … с надежным набором тестов, вы можете делать большие изменения, уверенные, что внешнее поведение останется тем же
Что мы должны тестировать?
Если возможно, все в нашем кодовой базе, каждая функция. Но это зависит от выбора разработчиков. Вы можете пропустить это, если непрактично написать надежный тест. Как сказал Ник Когхлан в гостевой сессии – … с надежным набором тестов, вы можете делать большие изменения, уверенные, что внешнее поведение останется неизменным
Unit testing
Метод, с помощью которого отдельные единицы исходного кода тестируются. Wikipedia говорит В компьютерном программировании, unit testing — это метод, с помощью которого отдельные единицы исходного кода, наборы из одного или нескольких модулей компьютерной программы вместе с соответствующими управляющими данными, процедурами использования и процедурами эксплуатации, тестируются для определения, подходят ли они для использования.
unittest module
В Python у нас есть модуль unittest
для помощи нам.
Код для вычисления факториала
В этом примере мы создадим файл factorial.py.
import sys
def fact(n):
"""
Функция вычисления факториала
:arg n: Число
:returns: факториал числа n
"""
if n == 0:
return 1
return n * fact(n -1)
def div(n):
"""
Просто деление
"""
res = 10 / n
return res
def main(n):
res = fact(n)
print(res)
if __name__ == '__main__':
if len(sys.argv) > 1:
main(int(sys.argv[1]))
Результат
$ python factorial.py 5
Какую функцию тестировать?
Как вы можете видеть, fact(n)
— это функция, которая выполняет все вычисления, поэтому
мы должны протестировать её, по крайней мере.
Наш первый тестовый случай
Теперь мы напишем наш первый тестовый случай.
import unittest
from factorial import fact
class TestFactorial(unittest.TestCase):
"""
Наш основной тестовый класс
"""
def test_fact(self):
"""
Фактический тест.
Любой метод, начинающийся с ``test_``, будет считаться тестовым случаем.
"""
res = fact(5)
self.assertEqual(res, 120)
if __name__ == '__main__':
unittest.main()
Запуск теста:
$ python factorial_test.py
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
Описание
Сначала импортируем модуль unittest
, а затем необходимые функции,
которые мы хотим протестировать.
Тест-кейс создается путем создания подкласса unittest.TestCase
.
Теперь откройте тестовый файл и измените 120 на 121 и посмотрите, что произойдет :)
Разные assert-утверждения
Метод Проверяет, что Новое в assertEqual(a, b) a == b
assertNotEqual(a, b) a != b
assertTrue(x) bool(x) is True
assertFalse(x) bool(x) is False
assertIs(a, b) a is b
2.7 assertIsNot(a, b) a is not b
2.7 assertIsNone(x) x is None
2.7 assertIsNotNone(x) x is not None
2.7 assertIn(a, b) a in b
2.7 assertNotIn(a, b) a not in b
2.7 assertIsInstance(a, b) isinstance(a, b)
2.7 assertNotIsInstance(a, b) not isinstance(a, b)
2.7
Тестирование исключений
Если мы вызовем div(0)
в factorial.py, мы можем увидеть, вызывает ли он исключение.
Мы также можем тестировать эти исключения, например:
self.assertRaises(ZeroDivisionError, div, 0)
Полный код
import unittest
from factorial import fact, div
class TestFactorial(unittest.TestCase):
"""
Наш основной тестовый класс
"""
def test_fact(self):
"""
Фактический тест.
Любой метод, который начинается с ``test_``, будет считаться тестовым случаем.
"""
res = fact(5)
self.assertEqual(res, 120)
def test_error(self):
"""
Тест на вызов исключения из-за ошибки времени выполнения
"""
self.assertRaises(ZeroDivisionError, div, 0)
## mounttab.py
Здесь у нас только одна функция mount_details(), которая выполняет разбор и вывод деталей монтирования.
import os
def mount_details():
"""
Выводит детали монтирования
"""
if os.path.exists('/proc/mounts'):
fd = open('/proc/mounts')
for line in fd:
line = line.strip()
words = line.split()
print()'%s on %s type %s' % (words[0],words[1],words[2]), end=' ')
if len(words) > 5:
print('(%s)' % ' '.join(words[3:-2]))
else:
print('')
if __name__ == '__main__':
mount_details()
После рефакторинга
Теперь мы рефакторили код и имеем одну новую функцию parse_mounts, которую мы можем легко протестировать.
import os
def parse_mounts():
"""
Она парсит /proc/mounts и возвращает список кортежей
"""
result = []
if os.path.exists('/proc/mounts'):
fd = open('/proc/mounts')
for line in fd:
line = line.strip()
words = line.split()
if len(words) > 5:
res = (words[0],words[1],words[2], '(%s)' % ' '.join(words[3:-2]))
else:
res = (words[0],words[1],words[2])
result.append(res)
return result
def mount_details():
"""
Печатает детали монтирования
"""
result = parse_mounts()
for line in result:
if len(line) == 4:
print('%s on %s type %s %s' % line)
else:
print('%s on %s type %s' % line)
if __name__ == '__main__':
mount_details()
и тестовый код для этого.
#!/usr/bin/env python
import unittest
from mounttab2 import parse_mounts
class TestMount(unittest.TestCase):
"""
Наша основная тестовая класс
"""
def test_parsemount(self):
"""
Фактический тест.
Любой метод, который начинается с ``test_``, будет считаться тестовым случаем.
"""
result = parse_mounts()
self.assertIsInstance(result, list)
self.assertIsInstance(result[0], tuple)
def test_rootext4(self):
"""
Тест для поиска корневой файловой системы
"""
result = parse_mounts()
for line in result:
if line[1] == '/' and line[2] != 'rootfs':
self.assertEqual(line[2], 'ext4')
if __name__ == '__main__':
unittest.main()
$ python mounttest.py
..
----------------------------------------------------------------------
Ran 2 tests in 0.001s
OK
Покрытие тестами
Покрытие тестами — это простой способ найти непротестированные части кодовой базы. Он не говорит вам, насколько хороши ваши тесты.
В Python у нас уже есть хороший инструмент для покрытия тестами, который может нам помочь. Вы можете установить его в Fedora
# yum install python-coverage
Или используя pip.
$ pip install coverage
Пример покрытия
$ coverage -x mounttest.py
<ВЫВОД убран>
$ coverage -rm
Name Stmts Miss Cover Missing
-----------------------------------------
mounttab2 21 7 67% 16, 24-29, 33
mounttest 14 0 100%
-----------------------------------------
TOTAL 35 7 80%