Spec-Zone.ru › NumPy 1.16

Руководство по тестированию

Введение

До релиза 1.15 NumPy использовал фреймворк для тестирования nose, теперь используется фреймворк pytest. Старый фреймворк всё ещё поддерживается для поддержки проектов, использующих старый фреймворк numpy, но все тесты для NumPy должны использовать pytest.

Наша цель — обеспечить тщательный набор модульных тестов для каждого модуля и пакета в SciPy и NumPy. Эти тесты должны проверять полную функциональность данной функции, а также её устойчивость к ошибочным или неожиданным входным аргументам. Многолетний опыт показывает, что лучшее время для написания тестов — до написания или изменения кода, это разработка через тестирование. Аргументы в пользу этого могут показаться абстрактными, но мы уверяем вас, что написание тестов в первую очередь приведёт к более надёжному и лучше спроектированному коду. Хорошо спроектированные тесты с хорошим покрытием значительно упрощают рефакторинг. Всякий раз, когда в процедуре обнаруживается новая ошибка, необходимо написать новый тест для этого конкретного случая и добавить его в набор тестов, чтобы предотвратить повторное появление этой ошибки незамеченной.

Для запуска полного набора тестов SciPy используйте следующее:

or from the command line:

SciPy использует фреймворк для тестирования из NumPy (в частности, numpy-testing), поэтому все примеры SciPy, показанные здесь, также применимы к NumPy. Полный набор тестов NumPy можно запустить следующим образом:

Метод тестирования может принимать два или более аргументов; первый, label — строка, указывающая, что должно быть протестировано, а второй, verbose — целое число, задающее уровень подробности вывода. Подробности см. в строке документации для numpy.test. Значение по умолчанию для label — ‘fast’ — это запуск стандартных тестов. Строка ‘full’ запустит полный набор тестов, включая те, которые определены как медленные. Если verbose равно 1 или меньше, тесты будут отображать только информационные сообщения о запускаемых тестах; но если оно больше 1, тесты также будут выдавать предупреждения о недостающих тестах. Поэтому, если вы хотите запустить каждый тест и получить сообщения о модулях, у которых нет тестов:

Наконец, если вы заинтересованы только в тестировании подмножества SciPy, например, модуля integrate, используйте следующее:

or from the command line:

Остальная часть этой страницы даст вам общее представление о том, как добавить модульные тесты в модули SciPy. Для нас крайне важно иметь обширное модульное тестирование, поскольку этот код будут использовать учёные и исследователи, и его разрабатывает большое количество людей по всему миру. Поэтому, если вы пишете пакет, который вы хотели бы включить в SciPy, пожалуйста, пишите тесты по мере разработки пакета. Кроме того, поскольку большая часть SciPy — это legacy-код, который изначально был написан без модульных тестов, есть ещё несколько модулей, у которых пока нет тестов. Пожалуйста, не стесняйтесь выбрать один из этих модулей и разработать тесты для него по мере изучения данного введения.

Написание собственных тестов

Каждый Python-модуль, модуль расширения или подпакет в каталоге пакета SciPy должен иметь соответствующий test_<name>.py файл. Pytest проверяет эти файлы на наличие методов тестирования (с именами test*) и классов тестирования (с именами Test*).

Предположим, у вас есть SciPy-модуль scipy/xxx/yyy.py с функцией zzz(). Для тестирования этой функции вы создадите модуль теста с именем test_yyy.py. Если вам нужно проверить только один аспект zzz, вы можете просто добавить функцию тестирования:

Чаще всего нам нужно сгруппировать несколько тестов вместе, поэтому мы создаём класс тестирования:

В этих методах тестирования assert_() и связанные функции используются для проверки того, справедливо ли какое-либо предположение. Если проверка не пройдёт, тест завершится неудачей. Обратите внимание, что встроенная в Python функция assert не должна использоваться, так как она удаляется во время компиляции с помощью -O.

Обратите внимание, что функции или методы test_ не должны иметь строки документации, так как это затрудняет идентификацию теста из результатов работы набора тестов с verbose=2 (или аналогичным уровнем подробности). Используйте обычные комментарии (#) при необходимости.

Иногда удобно запускать test_yyy.py отдельно, поэтому мы добавляем

at the bottom.

Маркировка тестов

В качестве альтернативы pytest.mark.<label>, вы можете использовать несколько меток.

Немаркированные тесты, подобные вышеуказанным, запускаются в стандартном режиме scipy.test(). Если вы хотите отметить свой тест как медленный и, следовательно, предназначенный для полного запуска scipy.test(label='full'), вы можете пометить его декоратором:

Аналогично для методов:

Доступные метки:

  • slow: отмечает тест как требующий много времени
  • setastest(tf): обходной путь для обнаружения теста, когда имя теста не соответствует формату
  • skipif(condition, msg=None): пропускает тест, когда eval(condition) равно True
  • knownfailureif(fail_cond, msg=None): позволит избежать запуска теста, если eval(fail_cond) равно True, полезно для тестов, которые условно приводят к сегментуляции памяти
  • deprecated(conditional=True): фильтрует предупреждения об устаревших функциях, генерируемые в тесте
  • paramaterize(var, input): альтернатива pytest.mark.paramaterized

Проще функции/методы настройки и завершения

Тестирование ищет функции настройки и завершения на уровне модуля или класса по имени; таким образом:

Функции настройки и завершения для функций и методов известны как «фиксатуры», и их использование не рекомендуется.

Параметрические тесты

Очень удобная функция тестирования позволяет легко тестировать различные параметры — сложная проблема для стандартных модульных тестов. Используйте декоратор dec.paramaterize.

Доктосты

Доктосты — удобный способ документировать поведение функции и одновременно тестировать это поведение. Вывод интерактивной Python-сессии можно включить в строку документации функции, и фреймворк для тестирования может запустить пример и сравнить фактический вывод с ожидаемым.

Доктосты можно запустить, добавив аргумент doctests к вызову test(); например, для запуска всех тестов (включая доктосты) для numpy.lib:

Доктосты выполняются как будто они находятся в новой инстанции Python, которая выполнила import numpy as np. Тесты, которые являются частью подпакета SciPy, будут иметь этот подпакет, уже импортированный. Например, для теста в scipy/linalg/tests/, пространство имён будет создано таким образом, что from scipy import linalg уже выполнилось.

tests/

Вместо того, чтобы хранить код и тесты в одном каталоге, мы помещаем все тесты для данного подпакета в подкаталог tests/. Для нашего примера, если он ещё не существует, вам нужно создать каталог tests/ в scipy/xxx/.

Таким образом, путь к test_yyy.py составляет scipy/xxx/tests/test_yyy.py.

После написания scipy/xxx/tests/test_yyy.py, тесты можно запустить, перейдя в каталог tests/ и набрав:

Или, если вы добавите scipy/xxx/tests/ в путь Python, вы можете запустить тесты интерактивно в интерпретаторе следующим образом:

__init__.py и setup.py

Однако обычно нежелательно добавлять каталог tests/ в путь Python. Лучше вызвать тест непосредственно из модуля xxx. Для этого просто поместите следующие строки в конец файла __init__.py вашего пакета:

Вам также необходимо добавить каталог тестов в секцию конфигурации вашего setup.py:

Теперь вы можете выполнить следующие действия для тестирования вашего модуля:

Также при запуске всего набора тестов SciPy ваши тесты будут найдены и выполнены:

Советы и хитрости

Создание многих похожих тестов

Если у вас есть набор тестов, которые необходимо запускать многократно с небольшими изменениями, может быть полезно создать базовый класс, содержащий все общие тесты, а затем создать подкласс для каждого варианта. Несколько примеров этой техники существуют в NumPy; ниже приведены выдержки из одного из них в numpy/linalg/tests/test_linalg.py:

В этом случае мы хотели проверить решение проблемы линейной алгебры с матрицами нескольких типов данных, используя linalg.solve и linalg.inv. Общие тестовые случаи (для матриц одинарной точности, двойной точности и т. д.) собраны в LinalgTestCase.

Известные ошибки и пропуск тестов

Иногда вы можете захотеть пропустить тест или пометить его как известную ошибку, например, когда набор тестов создаётся до кода, который он должен проверять, или если тест завершается неудачей только на определённой архитектуре. Для этого можно использовать декораторы из numpy.testing.dec.

Чтобы пропустить тест, просто используйте skipif:

Тест помечается как пропущенный, если SkipMyTest имеет ненулевое значение, а сообщение в выводе подробных результатов тестирования — это второй аргумент, переданный в skipif. Аналогично, тест можно пометить как известную ошибку с помощью knownfailureif:

Конечно, тест можно безусловно пропустить или пометить как известную ошибку, передав True в качестве первого аргумента к skipif или knownfailureif, соответственно.

Общее количество пропущенных и известных ошибок теста отображается в конце выполнения теста. Пропущенные тесты помечаются как 'S' в результатах тестирования (или 'SKIPPED' для verbose > 1), а известные ошибки теста помечаются как 'K' (или 'KNOWN' при verbose > 1).

Тесты на случайных данных

Тесты на случайных данных хороши, но поскольку сбои в тестах предназначены для выявления новых ошибок или регрессий, тест, который проходит большую часть времени, но изредка терпит неудачу без изменений кода, не является полезным. Сделайте случайные данные детерминированными, установив начальное значение генератора случайных чисел перед его генерацией. Используйте либо встроенную функцию Python random.seed(some_number), либо функцию NumPy numpy.random.seed(some_number), в зависимости от источника случайных чисел.

© 2005–2019 NumPy Developers
Licensed under the 3-clause BSD License.
https://docs.scipy.org/doc/numpy-1.16.1/reference/testing.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API