Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Современные средства разработки программного обеспечения. Учебное пособие

.pdf
Скачиваний:
1
Добавлен:
08.09.2026
Размер:
2 Мб
Скачать
☆
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,ac­tual_value)
Утверждает, что expected_value == ac­tual_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.
======================================================================
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]