Как тестировать программу в python

Программы выдают ошибки, когда, например, вводится неправильный ввод. Из-за этого нужно удостовериться, что выдается ошибка, когда вводится неправильный ввод. Из-за этого нам нужно проверить точное исключение, для этого примера мы будем использовать следующее исключение:

class WrongInputException(Exception): pass

Это исключение возникает, когда вводится неправильный ввод, в следующем контексте, где мы всегда ожидаем число в качестве ввода текста.

def convert2number(random_input): try: my_input = int(random_input) except ValueError: raise WrongInputException(«Expected an integer!») return my_input

Для того, чтобы проверить , было ли поднято исключение, мы используем assertRaises для проверки этого исключения. assertRaises можно использовать двумя способами:

  1. Используя обычный вызов функции. Первый аргумент принимает тип исключения, второй — вызываемый (обычно это функция), а остальные аргументы передаются этому вызываемому.
  2. Использование with пунктом, давая только тип исключения для функции. Преимущество этого заключается в том, что можно выполнять больше кода, но его следует использовать с осторожностью, поскольку несколько функций могут использовать одно и то же исключение, которое может быть проблематичным. Пример: с self.assertRaises (WrongInputException):

Сначала это было реализовано в следующем тестовом примере:

PYTHON PYTEST. ОСНОВЫ. 1 ЧАСТЬ


import unittest class ExceptionTestCase(unittest.TestCase): def test_wrong_input_string(self): self.assertRaises(WrongInputException, convert2number, «not a number») def test_correct_input(self): try: result = convert2number(«56») self.assertIsInstance(result, int) except WrongInputException: self.fail()

Также может возникнуть необходимость проверить исключение, которое не должно быть выброшено. Тем не менее, тест автоматически завершится неудачей, когда возникнет исключение, и, следовательно, может не потребоваться вообще. Просто чтобы показать варианты, второй метод тестирования показывает случай, когда можно проверить исключение, которое не должно быть выброшено. В основном, это поймать исключение , а затем проваливать испытание с использованием fail методы.

Перемешивание функций с помощью unittest.mock.create_autospec

Один из способов для имитации функции заключается в использовании create_autospec функции, которая будет макет из объекта в соответствии с его характеристиками. С помощью функций мы можем использовать это, чтобы гарантировать, что они вызываются соответствующим образом.

С функцией multiply в custom_math.py :

def multiply(a, b): return a * b

А функция multiples_of в process_math.py :

from custom_math import multiply def multiples_of(integer, *args, num_multiples=0, **kwargs): «»» :rtype: list «»» multiples = [] for x in range(1, num_multiples + 1): «»» Passing in args and kwargs here will only raise TypeError if values were passed to multiples_of function, otherwise they are ignored. This way we can test that multiples_of is used correctly. This is here for an illustration of how create_autospec works. Not recommended for production code. «»» multiple = multiply(integer,x, *args, **kwargs) multiples.append(multiple) return multiples

Python Language

Unit тесты в Python. Тестирование кода | Базовый курс. Программирование на Python


Тестирование устройства

Для Python существует несколько модулей тестирования. В этом разделе документации описывается базовый модуль unittest . Другие инструменты тестирования включают py.test и nosetests . В этой документации по тестированию python сравниваются некоторые из этих инструментов без углубления.

Исключения для тестирования

Программы выдают ошибки, если, например, задан неправильный ввод. Из-за этого необходимо убедиться, что при выдаче фактического неправильного ввода возникает ошибка. Из-за этого нам нужно проверить точное исключение, для этого примера мы воспользуемся следующим исключением:

class WrongInputException(Exception): pass

Это исключение возникает, когда вводится неправильный ввод, в следующем контексте, где мы всегда ожидаем число как ввод текста.

def convert2number(random_input): try: my_input = int(random_input) except ValueError: raise WrongInputException(«Expected an integer!») return my_input

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

  1. Использование обычного вызова функции. Первый аргумент принимает тип исключения, второй — вызываемый (обычно функция), а остальные аргументы передаются этому вызываемому.
  2. Использование предложения with , предоставляющее только тип исключения для этой функции. У этого есть преимущество, что больше кода может быть выполнено, но его следует использовать с осторожностью, поскольку несколько функций могут использовать одно и то же исключение, которое может быть проблематичным. Пример: с self.assertRaises (WrongInputException): convert2number («not number»)

Это было реализовано в следующем тестовом примере:

import unittest class ExceptionTestCase(unittest.TestCase): def test_wrong_input_string(self): self.assertRaises(WrongInputException, convert2number, «not a number») def test_correct_input(self): try: result = convert2number(«56») self.assertIsInstance(result, int) except WrongInputException: self.fail()

Также может потребоваться проверка исключения, которое не должно было быть выбрано. Тем не менее, тест будет автоматически терпеть неудачу при возникновении исключения и, следовательно, может вообще не понадобиться. Просто чтобы показать параметры, второй метод тестирования показывает случай, как можно проверить, не исключено ли исключение. В принципе, это ловушка исключения, а затем отказ от теста с использованием метода fail .

Смысловые функции с unittest.mock.create_autospec

Один из способов издеваться над функцией — использовать функцию create_autospec , которая будет издеваться над объектом в соответствии со своими спецификациями. С помощью функций мы можем использовать это, чтобы гарантировать, что они вызываются надлежащим образом.

С функцией multiply в custom_math.py :

def multiply(a, b): return a * b

И функция multiples_of в process_math.py :

from custom_math import multiply def multiples_of(integer, *args, num_multiples=0, **kwargs): «»» :rtype: list «»» multiples = [] for x in range(1, num_multiples + 1): «»» Passing in args and kwargs here will only raise TypeError if values were passed to multiples_of function, otherwise they are ignored. This way we can test that multiples_of is used correctly. This is here for an illustration of how create_autospec works. Not recommended for production code. «»» multiple = multiply(integer,x, *args, **kwargs) multiples.append(multiple) return multiples

Тестирование в Python – объектно-ориентированный и процедурный подход

Тестирование – головная боль для любого разработчика. Каждый (или почти каждый) готов согласиться с тем, что тестирование необходимо, и абсолютно у каждого имеется парочка «уважительных причин», чтобы не писать тесты. В компилируемых языках со статической типизацией (например, C++) часть работы по проверке корректности кода «берет на себя» компилятор; концентрированным выражением идеи «языка, на котором нельзя написать ошибочный код» стал язык Ада – прямо скажем, не самый популярный среди программистов. В динамических языках, таких, как Python или Perl на этапе компиляции происходит самая минимальная проверка исходного кода, поэтому возникает необходимость (на радость адептам пресловутой методологии Test Driven Development ‒ «разработка через тестирование») тестировать буквально каждую строчку.

К счастью, создатели динамических языков программирования поняли это достаточно быстро, и стали включать в свои стандартные библиотеки средства тестирования. В языке Python используются сразу две системы тестирования: doctest и unittest; doctest – вещь достаточно специфическая, и, хотя у нее есть свои достоинства и свои сторонники, ее рассмотрение выходит за рамки настоящей статьи. Инфраструктура unittest (ее исходный код находится в модуле unittest стандартной библиотеки Python) гораздо привычнее, особенно для тех, кто уже имел дело с JUnit, DUnit и прочими xUnit.

unittest и funtest

Рассмотрим небольшой пример использования unittest. Те, кому не по душе объектно-ориентированное программирование – а такие еще встречаются, причем не только меж закостенелых «динозавров» времен Алгола, и среди них, кстати, попадаются неплохие специалисты – могут его пропустить.

import unittest from unittest import TestCase, main import my_math from my_math import factorial class FactorialTestCase(TestCase): def test_fact_0(self): «»»test factorial(0)»»» self.assertEquals(factorial(0), 1) def test_fact_1(self): «»»test factorial(1)»»» self.assertEquals(factorial(1), 1) def test_fact_2(self): «»»test factorial(2)»»» self.assertEquals(factorial(2), 2) def test_fact_3(self): «»»test factorial(3)»»» self.assertEquals(factorial(3), 6) if __name__ == ‘__main__’: main()

В этом примере мы тестируем функцию factorial, которую импортируем из некоторого модуля my_math.

Для тестирования мы импортируем из модуля unittest класс TestCase, создаем производный от него класс FactorialTestCase, определяем в классе FactorialTestCase несколько тестов. Каждый тест – это метод с единственным аргументом self, имя метода обязательно должно начинаться с префикса test (вполне логичное соглашение об именованиях).

Далее мы вызываем определенную в модуле unittest функцию main, которая находит созданный нами класс, в нем находит все тесты (т.е. все методы, имена которых начинаются с приставки «test»), создает столько экземпляров класса, сколько в нем определено таких методов, и для каждого экземпляра класса вызывает один и только один из этих методов. В этом примере функция main создает четыре экземпляра класса FactorialTestCase.

Для одного будет вызван метод test_fact_0, для другого – test_fact_1 и т.д. Если в ходе прохождения какого-либо теста возникает исключение, то такой тест считается неуспешным. При выполнении функции main все исключения перехватываются, неуспешные тесты регистрируются, а по окончании тестирования на экран выводится отчет, в котором указывается, сколько тестов было проведено, и какие из них оказались неуспешными. Метод assertEquals унаследован классом FactorialTestCase от своего «родителя» – класса TestCase, и всего-навсего проверяет, равны ли между собой две величины (в данном случае – результат, возвращаемый функцией factorial, и ожидаемое нами значение результата). Если они не равны, метод assertEquals генерирует исключение AssertionError; как уже говорилось, при выполнении функции main, все исключения возникающие в ходе тестирования перехватываются, а по окончании тестирования, выдается отчет о возникших проблемах.

Необходимо еще сказать пару слов о комментариях (точнее говоря, строках документации, в англоязычной литературе по Python называемых docstrings) – это тот самый текст в забавных «тройных» кавычках, который находится между заголовком метода и его телом. В unittest этот текст выводится в отчете об ошибках для того, чтобы сообщить программисту, какие именно тесты были неуспешными. Для теста, у которого строка документации отсутствует, будет выведено имя теста, что уже не так наглядно, поэтому лучше не лениться писать эти самые docstring. Кстати, по этой же причине не стоит «забивать» в один тест слишком много разных проверок – с тем же успехом можно вообще отказаться от использования unittest и писать все тесты в виде одного скрипта «в стиле Акына», без разбиения на структурные единицы. При этом мы, конечно, получим информацию о том, что при тестировании что-то пошло не так, но вот найти ответ на вопрос, где именно произошла ошибка, будет уже намного труднее.

Дополнительно unittest предоставляет возможность реализовать метод, «подготавливающий» систему к каждому новому тесту (метод setUp), и метод, выполняющий освобождение ресурсов (например, удаление временных файлов) после каждого теста, независимо от того, успешен тест или нет (метод tearDown).

Для «чистых» функций (т.е. тех функций, которые не имеют сторонних эффектов, например, математических функций) это многообразие кажется излишним, а вот код получается довольно объемный – плата за универсальность и объектно-ориентированный подход. Это обстоятельство и побудило меня написать – нет, не новую инфраструктуру, всего лишь процедурно-ориентированный интерфейс к unittest, «заточенный» для работы с «чистыми» функциями. Поймите меня правильно, объектно-ориентированное программирование – вещь хорошая, но всякая хорошая вещь хороша на своем месте.

Вот как будет выглядеть предыдущий пример при использовании предлагаемого процедурно-ориентированного модуля funtest:

import funtest from funtest import add_OK_test, do_tests import my_math from my_math import factorial def main() add_OK_test(factorial, 1, 0) add_OK_test(factorial, 1, 1) add_OK_test(factorial, 2, 2) add_OK_test(factorial, 6, 3) do_tests() if __name__ == ‘__main__’: main()

Выглядит такой код несколько проще, чем предыдущий, хотя, конечно, это было бы заметнее, если бы перед нами был файл, содержащий несколько сотен тестов. Впрочем, простота – понятие относительное, и кое-кто из адептов объектно-ориентированного программирования, глядя на этот код, пожмет плечами и скажет: «Очередной велосипед». Ну и ладно 😉

Рейтинг
( Пока оценок нет )
Загрузка ...
EFT-Soft.ru