Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Современные средства разработки программного обеспечения. Учебное пособие
.pdf
171
<class 'complex'>
11.5 Контрольные вопросы
1 Из каких источников можно получить данные?
2 Какие виды форматов данных используются чаще всего?
3 Какую роль играют форматы данных?
4 Перечислите пять распространенных форматов данных.
5 Для чего подходит использование внешних форматов данных?
6 Что такое системная интеграция?
7 Какие выделяют методы системной интеграции?
8 Что позволяет метод интеграции с общим форматом данных (CDF)?
9 Что представляет собой формат JSON, в каком виде представляет данные?
10 Какие основные типы данных поддерживает JSON?
11 Что называется сериализацией?
12 С помощью каких методов производится запись JSON в файл на Python?
13 Что представляет собой объект JSON, создаваемый с помощью сериализа-
ции на Python?
14 Какие способы можно использовать для сериализации пользовательских
типов на Python?

172
12 Инструменты автоматического тестирования в Python
12.1 Базовые понятия тестирования
При работе над большим проектом часто приходится обновлять определенные
модули и рефакторинг кода. Но такие изменения могут иметь непредвиденные последствия для других модулей, в которых используется обновленный модуль. Иногда
это может привести к нарушению существующей функциональности.
Разработчик должен протестировать свой код, чтобы убедиться, что все модули
в приложении работают так, как задумано.
Метод ручного тестирования неэффективен, когда нужно исчерпывающе про-
верить функцию с большим списком аргументов. В этом случае более эффективно
автоматическое тестирование, которое запускает и проверяет выходные данные
функции на соответствие тестовым примерам, определенным в наборе тестов.
Автоматическое тестирование – это выполнение плана тестирования (частей
приложения, которые нужно протестировать, порядка, в котором нужно их протестировать, и ожидаемых ответов) скриптом, а не человеком.
Python поставляется с набором инструментов и библиотек, которые помогут создавать автоматические тесты для приложения [49].
Модульные и интеграционные тесты
Тестирование нескольких компонентов известно, как интеграционное тести-
рование.
В данном тестировании нужно проанализировать все компоненты приложения
в целом, все те классы, функции и модули, из которых оно состоит, которые должны
работать правильно, чтобы простая задача давала правильный результат. Основная
проблема при интеграционном тестировании заключается в том, что очень сложно
диагностировать проблему, не имея возможности определить, какая часть системы
дает сбой.
Модульный тест – это тест меньшего размера, который проверяет правильность работы отдельного компонента. Модульный тест помогает быстрее выявить неисправности в приложении и устранить их.

173
Модульное тестирование является важной частью процесса разработки на ос-
нове тестирования (TDD), когда разработчики пишут тесты перед написанием фак-
тического кода. Этот процесс помогает гарантировать, что код соответствует требованиям к программному обеспечению, и способствует совершенствованию методов
проектирования при одновременном снижении сложности программного обеспечения.
В большинстве случаев модульные тесты автоматизированы, что позволяет разработчикам быстро и легко запускать их, выявлять дефекты в режиме реального времени и измерять покрытие кода.
Покрытие кода тестами – это показатель, который помогает разработчикам
определить процент кода, выполняемого во время тестирования, и помогает им определить области кода, которые могут нуждаться в дальнейшем тестировании [50].
Следует рассмотреть возможность использования инструмента покрытия
кода для измерения степени охвата кодовой базы модульными тестами. Такие инструменты, как coverage.py, могут помочь определить области кода, которым не хватает
тестового покрытия, гарантируя, что тесты будут всеобъемлющими и тщательными
[51].
Таким образом:
1 Интеграционный тест проверяет, работают ли компоненты приложения
друг с другом.
2 Модульный тест проверяет небольшой компонент в приложении.
Можно писать, как интеграционные, так и модульные тесты на Python.
Для Python доступно множество программ для тестирования, самые популярные
программы для выполнения тестов:
– unittest;
– nose (nose2);
– pytest.
unittest встроен в стандартную библиотеку Python начиная с версии 2.1.

174
Можно обнаружить, что со временем, по мере написания сотен или даже тысяч
тестов для приложения, становится все труднее понимать и использовать выходные
данные unittest.
nose совместим с любыми тестами, написанными с использованием фрейм-
ворка unittest, и может использоваться как дополнительная замена тестировщика
unittest. Разработка nose как приложения с открытым исходным кодом отстала,
и был создан инструмент под названием nose2. Рекомендуется использовать nose2
вместо nose.
pytest поддерживает выполнение тестовых примеров unittest. Реальное
преимущество pytest заключается в написании pytest тестовых примеров. Тесто-
вые примеры pytest представляют собой серию функций в файле Python, начинаю-
щихся с имени test_.
pytest имеет несколько других функций:
– Поддержка встроенного assert оператора вместо использования специальных методов self.assert*().
– Поддержка фильтрации тестовых примеров.
– Возможность повторного запуска с последнего неудачного теста.
– Экосистема из сотен плагинов для расширения функциональности.
Важно выбрать инструмент тестирования, соответствующий требованиям и
уровню опыта программиста.
12.2 Работа с unittest
При работе с unittest требуется, чтобы:
– тесты помещали в классы как методы;
– использовать серию специальных методов утверждения в классе
unittest.TestCase вместо встроенного оператора assert.
Проверка выходных данных на соответствие известному ответу называется
утверждением.

175
Существует несколько общих рекомендаций по написанию утверждений:
1 Убедиться, что тесты повторяемы, и запустить тест несколько раз, чтобы
увидеть, что каждый раз он дает один и тот же результат.
2 Подтвердить результаты, относящиеся к входным данным.
Общая процедура настройки модульных тестов в Python выглядит следующим
образом [52]:
# <module-name>.py
import unittest
from moduleName import function_to_test
class TestClass(unittest.TestCase):
def test_name_1(self):
# check function_to_test
def test_name_2(self):
# check function_to_test
def test_name_n(self):
# check function_to_test
Приведенный выше фрагмент кода <module-name>.py выполняет следующее:
– Импортирует встроенный в Python модуль unittest.
– Импортирует тестируемую функцию Python, function_to_test из модуля
moduleName, в котором она определена.
– Создает тестовый класс (TestClass), который наследуется от класса
unittest.TestCase.
– Каждый из тестов, которые следует запускать, должен быть определен как
метод внутри тестового класса.
Чтобы модуль unittest идентифицировал эти методы как тесты и запускал их,
имена этих методов должны начинаться с test_.
Класс TestCase в unittest модуле предоставляет полезные методы утверждения,
позволяющие проверить, возвращает ли тестируемая функция ожидаемые значения.
В таблице 12.1 перечислены некоторые наиболее распространенные методы
утверждения.

176
Таблица 12.1 – Методы утверждения
Метод
Описание
assertEqual(expected_value,actual_value)
Утверждает, что expected_value == actual_value
assertTrue(result)
Утверждает, что bool(result) является True
assertFalse(result)
Утверждает, что bool(result) является
False
assertRaises(exception, function,
*args, **kwargs)
Утверждает, что function(*args,
**kwargs) повышает exception
Существуют также другие методы, используемые для выполнения более специфических проверок, например, функция для проверки того, почти ли равны два заданных значения или нет:
assertAlmostEqual(first, second, places=7, msg=None, delta=None)
Эта функция проверяет, на приближенное равенство первого и второго аргументов, вычисляя разницу, округляя до заданного количества десятичных знаков
places (по умолчанию 7) и сравнивая их с нулем.
Если вместо places указана погрешность delta, то разница между первым и вторым должна быть меньше или равна delta.
Для получения полного списка методов утверждения можно обратиться к доку-
ментации по unittest [53].
Для запуска тестов нужно запустить unittest в качестве основного модуля, используя следующую команду:
python -m unittest <module-name>.py
Можно добавить if __name__=='__main__' условие для запуска unittest в качестве основного модуля.
if __name__=='__main__':
unittest.main()
Добавление приведенного выше условия позволит запускать тесты, напрямую
запуская модуль Python, содержащий тесты.
python <module-name>.py

177
Пример. Пусть в модуле prime_number определена простая функция is_prime(),
которая принимает число number и проверяет, является ли оно простым.
import math
def is_prime(number):
for i in range(2,int(math.sqrt(number))+1):
if number %i==0:
return False
return True
Чтобы протестировать функцию is_prime(), возвращающую логическое значение, можно использовать методы assertTrue() и assertFalse().
Для этого нужно определить методы тестирования внутри TestPrime класса, ко-
торый наследуется от unittest.TestCase.
import unittest
from prime_number import is_prime
class TestPrime(unittest.TestCase):
def test_prime_not_prime(self):
self.assertTrue(is_prime(2))
self.assertTrue(is_prime(5))
self.assertFalse(is_prime(9))
self.assertTrue(is_prime(11))
Запуск модуля:
python test_prime.py
Результат работы модуля:
Output
.
---------------------------------------------------------------
Ran 1 test in 0.001s
OK
После запуска модуля test_prime видно, что один тест был выполнен успешно.
Если какой-либо из методов assert выдает ошибку AssertionError, то тест завершается
неудачей.
Модульные тесты для проверки на наличие исключений

178
В рассмотренном примере не учтено, что аргументы в вызове функции
is_prime() должны быть натуральными числами. Можно использовать подсказку типа
для принудительного применения типов или вызывать исключения для недопустимых входных данных.
При тестировании функции is_prime() не учтено следующее:
- Для аргумента с плавающей запятой is_prime() функция все равно будет вы-
полняться и возвращать True or False, что неверно.
- Для аргументов других типов, скажем, строки 'two' вместо числа 2, функция
выдает ошибку типа TypeError.
- Если аргумент является отрицательным целым числом, то math.sqrt() функ-
ция выдает ошибку ValueError. Квадраты всех действительных чисел (положительных, отрицательных или нулевых) всегда неотрицательны. Таким образом, квадратный корень определяется только для неотрицательных чисел.
Чтобы решить вышеизложенное, нужно проверить значение параметра, используемое в вызове функции для аргумента number и создать исключения по мере необходимости, т.е.:
− проверить, является ли number целым числом. Если да, перейти к следую-
щей проверке. В противном случае создать исключение TypeError;
− проверить, является ли number отрицательным целым числом. Если да, со-
здать исключение ValueError.
Изменив определение функции для проверки входных данных и создания исключений, получили:
import math
def is_prime(number):
if type(number) != int:
raise TypeError('Не соответствие типа!)
if number < 0:
raise ValueError(Число должно быть положительным!')
for i in range(2,int(math.sqrt(number))+1):
if number%i==0:
return False

179
return True
Следующий шаг – проверить, возникают ли добавленные исключения.
Для тестирования исключений используют метод assertRaises().
Рисунок 12.1 – Генерация исключений
Для проверки, создаются ли исключения в определении класса TestPrime нужно
добавить методы:
− test_typeerror_1() и test_typeerror_2() для проверки, вызывается ли исключе-
ние TypeError;
− test_valueerror() для проверки, вызывается ли исключение ValueError.
Для вызова метода assertRaises() можно использовать следующий общий синтаксис:
def test_exception(self):
self.assertRaises(exception-name,function-name,args)
Можно также использовать синтаксис с помощью context manager (будет использовано в этом примере):
def test_exception(self):
with self.assertRaises(exception-name):
function-name(args)
Добавив методы тестирования для проверки на наличие исключений, получили:
import unittest
from prime_number import is_prime
class TestPrime(unittest.TestCase):
def test_prime_not_prime(self):
self.assertTrue(is_prime(2))
self.assertTrue(is_prime(5))
self.assertFalse(is_prime(9))
self.assertTrue(is_prime(11))
def test_typeerror_1(self):
with self.assertRaises(TypeError):

180
is_prime(6.5)
def test_typeerror_2(self):
with self.assertRaises(TypeError):
is_prime('five')
def test_valueerror(self):
with self.assertRaises(ValueError):
is_prime(-4)
if __name__=='__main__':
unittest.main()
Запуск модуля test_prime:
python test_prime.py
Результат работы модуля:
Output
....
----------------------------------------------------------
Ran 4 tests in 0.002s
OK
В приведенных примерах, все тесты прошли успешно.
Для демонстрации неудачного теста можно модифицировать один из методов,
например, test_typeerror_2() следующим образом:
def test_typeerror_2(self):
with self.assertRaises(TypeError):
is_prime(5)
В этом методе функция is_prime() вызывается с числом 5 в качестве аргумента.
Здесь 5 – допустимое значение входного аргумента, для которого функция возвращает True. Следовательно, функция не создаст TypeError, и тогда при запуске нового
теста, будет виден один неудачный тест.
Запуск модуля test_prime:
python test_prime.py
Результат работы модуля:
Output
..F.
======================================================================
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
