Комментарии
Популярные По порядку
Не удалось загрузить комментарии.
ЛУЧШИЕ СТАТЬИ ПО ТЕМЕ
ООП на Python: концепции, принципы и примеры реализации
Программирование на Python допускает различные методологии, но в его основе лежит объектный подход, поэтому работать в стиле ООП на Python очень просто.
3 самых важных сферы применения Python: возможности языка
Существует множество областей применения Python, но в некоторых он особенно хорош. Разбираемся, что же можно делать на этом ЯП.
Программирование на Python: от новичка до профессионала
Пошаговая инструкция для всех, кто хочет изучить программирование на Python (или программирование вообще), но не знает, куда сделать первый шаг.
Источник: proglib.io
Unit-тесты в Python
Проверка текста на опечатки в python (питон)
B прошлой статье мы рассказывали о организации модулей Python, особенно полезных для крупных Data Science проектов. В этот раз поговорим о модульном тестировании (unit testing): читайте в нашей статье о том, как писать и запускать тесты для проверок функций и как в стандартной библиотеке Python применяется один из главных принципов разработки ПО — DRY (don’t repeat yourself).
Пишем простые тесты для функций модуля
Для тестирования будем использовать стандартную библиотеку unittest . Допустим, имеется Python-файл с двумя функциями:
# файл calc.py def add(x, y): return x + y def is_positive(x): return x > 0
Требуется написать тесты для этого файла (модуля), который проверит правильность разработанных функций. Для этого создадим файл tests.py, в котором будет класс с методом для тестирования. Важно, чтобы все методы с тестами начинались с test_ , иначе Python не поймет, что тестировать. После наследования от класса TestCase из unittest будут доступны методы, которые проверяют на соответствие ожидаемому результату. Напишем несколько проверок для функций вышеприведённого модуля:
# файл tests.py import unittest import calc # тестируемый модуль class TestCalc(unittest.TestCase): # начинается с test_ def test_add(self): self.assertEqual(calc.add(3, 6), 9) # начинается с test_ def test_is_positive(self): self.assertTrue(calc.is_positive(1))
Здесь два теста: один проверяет на равенство, другой на истину. Кроме того, имеются и другие виды проверок, которые показаны на рисунке ниже. Так, например, для сравнений чисел с плавающей точкой (float) рекомендуется использовать assertAlomstEqual .
Запуск unit-тестов
Для запуска тестов нужно написать в командой строке следующее:
$ python -m unittest tests.py
На экране выведется сообщение о проведении тестов и их статус. Каждый пройденный тест обозначается точкой . , а проваленной буквой F . В нашем случае получили две точки и статус ОК :
проверка ввода на число python (питон) #short
.. ———————————————————————- Ran 2 tests in 0.000s OK
Кроме того, чтобы не прописывать всю вышеприведённую строчку, можно добавить в Python-файл с тестами вызов функции unittest.main в конце в блоке __main__ , о котором говорили тут:
# файл tests.py class TestCalc(unittest.TestCase) # остальной код if __name__ == «__main__»: unittest.main()
Тогда запуск осуществляется простой командой:
$ python tests.py
Проваленные тесты
Попробуем заменить некоторые значения так, что ожидаемый результат не будет сходиться с вычислениями, например, изменим тест с проверкой на положительное число:
def test_is_positive(self): self.assertTrue(calc.is_positive(-1))
Ниже показано сообщение. В результате мы провалили один тест и получили .F и статус Failed .
.F ====================================================================== FAIL: test_is_positive (__main__.TestCalc) ———————————————————————- Traceback (most recent call last): File «tests.py», line 10, in test_is_positive self.assertTrue(calc.is_positive(-1)) AssertionError: False is not true ———————————————————————- Ran 2 tests in 0.001s FAILED (failures=1)
Если мы заменим в первом методе с проверкой на равенство ожидаемый результат на другое число, то провалим оба тести и получим FF . Ниже показано, как это выглядит.
def test_add(self): self.assertEqual(calc.add(3, 6), 10)
FF ====================================================================== FAIL: test_add (__main__.TestCalc) ———————————————————————- AssertionError: 9 != 10 ====================================================================== FAIL: test_is_positive (__main__.TestCalc) ———————————————————————- AssertionError: False is not true FAILED (failures=2)
Тестирование исключений
На практике может возникнуть ситуация, когда функция содержит исключение, срабатываемое при определенных условиях. Их тоже можно проверять, ведь, если функция Python поднимает исключение при ожидаемом результате, значит, она работает корректно.
Добавим в файл с вычислениями ещё одну функцию деления одного числа на другое. Понятно, что делить на 0 невозможно, поэтому функция поднимает соответствующее исключение:
# Файл calc.py def divide(x, y): if y == 0: raise ZeroDivisionError return x / y
Тогда для проверки исключений рекомендуется использовать контекстный менеджер Python, внутри которого просто вызвать проверяемую функцию:
def test_divide(self): with self.assertRaises(ZeroDivisionError): calc.divide(10, 0)
Операции перед проведением тестов
Порой требуется выполнить операции перед каждым тестом, особенно, если требуется протестировать методы класса, не создавая постоянно экземпляров класса. Более того, это позволит соблюсти принцип DRY (Don’t Repeat Youreself — не повторяйся). Пусть в файле person.py имеется класс Person , который хранит информацию о имени, фамилии и e-mail:
Воспользуемся методом setUp , который перед каждым тестом будет создавать экземпляр класса Person. В итоге, проверим правильность введенного e-mail и полного имени. Ниже приведён код на Python.
Помимо setUp , имеется метод tearDown , который, наоборот, запускается в конце каждого теста. Эти методы также пригодятся для открытия и закрытия файлов.
В следующей статье поговорим о продвинутых темах модульного тестирования. А о том, как писать тесты в реальных проектах Data Science, вы узнаете на наших Python-курсах в лицензированном учебном центре обучения и повышения квалификации IT-специалистов в Москве.
Источник: python-school.ru
Тестируем на Python: unittest и pytest. Инструкция для начинающих
Меня зовут Андрей Смирнов, я занимаюсь Python-разработкой, автоматизацией технических процессов и преподаю промышленное программирование в Школе программистов МШП.
Не секрет, что разработчики создают программы, которые рано или поздно становятся очень масштабными (если смотреть на количество строчек кода). А с этим приходит и большая ответственность за качество.
Сейчас расскажу, как unittest и pytest помогут найти ошибки в программах и исключить их в будущем.
Итак, тестирование
Каждый, кто писал первые программы (будь то классический «hello, world» или же калькулятор), всегда запускал тесты, чтобы проверить их работу.
Senior Python-разработчик КРОК , Москва, можно удалённо , По итогам собеседования
Сам факт запуска — самое первое, незримое касание технологии тестирования в вашей жизни. Рассмотрим его как процесс поиска ошибок на чуть более сложной программе.
Например, вам нужно ввести три числа (a, b, c) и найти корни квадратного уравнения. Для решения пишем код:
from math import sqrt def square_eq_solver(a, b, c): result = [] discriminant = b * b — 4 * a * c if discriminant == 0: result.append(-b / (2 * a)) else: result.append((-b + sqrt(discriminant)) / (2 * a)) result.append((-b — sqrt(discriminant)) / (2 * a)) return result def show_result(data): if len(data) > 0: for index, value in enumerate(data): print(f’Корень номер равен ‘) else: print(‘Уравнение с заданными параметрами не имеет корней’) def main(): a, b, c = map(int, input(‘Пожалуйста, введите три числа через пробел: ‘).split()) result = square_eq_solver(a, b, c) show_result(result) if __name__ == ‘__main__’: main()
Сразу оговорюсь: любую задачу, какой бы она ни была краткой, я рассматриваю с позиции «когда-нибудь она вырастет и станет очень объёмной». Поэтому всегда стараюсь разделять программу на различные подпрограммы (ввод/обработка/вывод).
Возможно, вы уже заметили ошибку в коде. Однако иногда она может быть скрыта настолько глубоко, что её просто так не обнаружишь. И в таком случае единственный способ вывести ее на свет — протестировать код. Как это сделать?
— зная алгоритм нахождения корней уравнения, определяем наборы входных данных, которые будут переданы на вход программе;
— зная входные данные, можно вручную просчитать, какой ответ должна дать программа;
— запускаем программу и передаем ей на вход исходные данные;
— получаем от нее ответ и сравниваем с тем, который должен быть получен. Если они совпадают — хорошо, идём к следующему набору данных, если нет, сообщаем об ошибке.
Например, для данной задачи можно подобрать следующие тесты:
- 10x**2 = 0 — единственный корень x=0
- 2x**2 + 5x — 3 = 0 — у такого уравнения два корня (x1 = 0.5, x2=-3)
- 10x**2+2 = 0 — у этого уравнения корней нет
Тесты подобрали, что дальше? Правильно, запускаем:
Тест номер 1 > python.exe example.py Пожалуйста, введите три числа через пробел: 10 0 0 Корень номер 0 равен 0.00 Тест номер 2: > python.exe example.py Пожалуйста, введите три числа через пробел: 2 5 -3 Корень номер 1 равен 0.50 Корень номер 2 равен -3.00 Тест номер 3: > python.exe example.py Пожалуйста, введите три числа через пробел: 10 0 2 Traceback (most recent call last): File «C:PyProjectstprogerexample.py», line 32, in main() File «C:PyProjectstprogerexample.py», line 27, in main result = square_eq_solver(a, b, c) File «C:PyProjectstprogerexample.py», line 11, in square_eq_solver result.append((-b + sqrt(discriminant)) / (2 * a)) ValueError: math domain error
Упс… В третьем тесте произошла ошибка. Как раз та, которую вы могли заметить в исходном коде программы — не обрабатывался случай с нулевым дискриминантом. В итоге, можно подкорректировать код функции так, чтобы этот вариант обрабатывался правильно:
def square_eq_solver(a, b, c): result = [] discriminant = b * b — 4 * a * c if discriminant == 0: result.append(-b / (2 * a)) elif discriminant > 0: #
Запускаем все тесты повторно и они срабатывают нормально.
Но учтите, чтобы повторно проверить программу, потребуется потратить несколько минут и снова проверить все три варианта входных значений. Если таких вариантов будет много, вызывать их вручную будет очень накладно. И здесь на сцену выходит автоматизированное тестирование.
Программа автоматического тестирования запускается на основе заранее заготовленных входных/выходных данных и программы, которая будет их вызывать. По сути, это программа, тестирующая другие программы. И в рамках экосистемы языка Python есть несколько пакетов, позволяющих автоматизировать процесс тестирования.
Что делает Python-разработчик в облаке
Unittest и pytest: пишем тесты
Две самые популярные библиотеки — unittest и pytest. Попробуем каждую, чтобы объективно оценить синтаксис.
Начнем с unittest, потому что именно с нее многие знакомятся с миром тестирования. Причина проста: библиотека по умолчанию встроена в стандартную библиотеку языка Python.
Формат кода
По формату написания тестов она сильно напоминает библиотеку JUnit, используемую в языке Java для написания тестов:
- тесты должны быть написаны в классе;
- класс должен быть отнаследован от базового класса unittest.TestCase;
- имена всех функций, являющихся тестами, должны начинаться с ключевого слова test;
- внутри функций должны быть вызовы операторов сравнения (assertX) — именно они будут проверять наши полученные значения на соответствие заявленным.
Пример использования unittest для нашей задачи
import unittest class SquareEqSolverTestCase(unittest.TestCase): def test_no_root(self): res = square_eq_solver(10, 0, 2) self.assertEqual(len(res), 0) def test_single_root(self): res = square_eq_solver(10, 0, 0) self.assertEqual(len(res), 1) self.assertEqual(res, [0]) def test_multiple_root(self): res = square_eq_solver(2, 5, -3) self.assertEqual(len(res), 2) self.assertEqual(res, [0.5, -3])
Запускается данный код следующей командой
python.exe -m unittest example.py
И в результате на экран будет выведено:
> python.exe -m unittest example.py . —————————————————————— Ran 3 tests in 0.001s OK
В случае, если в каком-нибудь из тестов будет обнаружена ошибка, unittest не замедлит о ней сообщить:
> python.exe -m unittest example.py F.. ================================================================== FAIL: test_multiple_root (hello.SquareEqSolverTestCase) —————————————————————— Traceback (most recent call last): File «C:PyProjectstprogerexample.py», line 101, in test_multiple_root self.assertEqual(len(res), 3) AssertionError: 2 != 3 —————————————————————— Ran 3 tests in 0.001s FAILED (failures=1)
Unittest: аргументы “за”
- Является частью стандартной библиотеки языка Python: не нужно устанавливать ничего дополнительно;
- Гибкая структура и условия запуска тестов. Для каждого теста можно назначить теги, в соответствии с которыми будем запускаться либо одна, либо другая группа тестов;
- Быстрая генерация отчетов о проведенном тестировании, как в формате plaintext, так и в формате XML.
Unittest: аргументы “против”
- Для проведения тестирования придётся написать достаточно большое количество кода (по сравнению с другими библиотеками);
- Из-за того, что разработчики вдохновлялись форматом библиотеки JUnit, названия основных функций написаны в стиле camelCase (например setUp и assertEqual);
- В языке python согласно рекомендациям pep8 должен использоваться формат названий snake_case (например set_up и assert_equal).
Pytest
Возможно, наиболее популярный фреймворк с открытым исходным кодом из всех, представленных здесь.
Pytest позволяет провести модульное тестирование (тестирование отдельных компонентов программы), функциональное тестирование (тестирование способности кода удовлетворять бизнес-требования), тестирование API (application programming interface) и многое другое.
Формат кода
Написание тестов здесь намного проще, нежели в unittest. Вам нужно просто написать несколько функций, удовлетворяющих следующим условиям:
- Название функции должно начинаться с ключевого слова test;
- Внутри функции должно проверяться логическое выражение при помощи оператора assert.
Пример использования pytest для нашей задачи:
def test_no_root(): res = square_eq_solver(10, 0, 2) assert len(res) == 0 def test_single_root(): res = square_eq_solver(10, 0, 0) assert len(res) == 1 assert res == [0] def test_multiple_root(): res = square_eq_solver(2, 5, -3) assert len(res) == 3 assert res == [0.5, -3]
Запускается данный код следующей командой
pytest.exe example.py
И в результате на экран будет выведено:
> pytest.exe example.py ======================= test session starts ====================== platform win32 — Python 3.9.6, pytest-7.1.2, pluggy-1.0.0 rootdir: C:PyProjectstproger collected 3 items example.py . [100%] ======================== 3 passed in 0.03s =======================
В случае ошибки вывод будет несколько больше:
> pytest.exe example.py ======================= test session starts ====================== platform win32 — Python 3.9.6, pytest-7.1.2, pluggy-1.0.0 rootdir: C:PyProjectstproger collected 3 items example.py ..F [100%] ============================ FAILURES ============================ _______________________ test_multiple_root _______________________ def test_multiple_root(): res = square_eq_solver(2, 5, -3) > assert len(res) == 3 E assert 2 == 3 E + where 2 = len([0.5, -3.0]) example.py:116: AssertionError ===================== short test summary info ==================== FAILED example.py::test_multiple_root — assert 2 == 3 =================== 1 failed, 2 passed in 0.10s ==================
Pytest: аргументы “за”
- Позволяет писать компактные (по сравнению с unittest) наборы тестов;
- В случае возникновения ошибок выводится гораздо больше информации о них;
- Позволяет запускать тесты, написанные для других тестирующих систем;
- Имеет систему плагинов (и сотни этих самых плагинов), расширяющую возможности фреймворка. Примеры таких плагинов: pytest-cov, pytest-django, pytest-bdd;
- Позволяет запускать тесты в параллели (при помощи плагина pytest-xdist).
Pytest: аргументы “против”
- pytest не входит в стандартную библиотеку языка Python. Поэтому его придётся устанавливать отдельно при помощи команды pip install pytest;
- совместимость кода с другими фреймворками отсутствует. Так что, если напишете код под pytest, запустить его при помощи встроенного unittest не получится.
Ну и что лучше?
- Если вам нужно базовое юнит-тестирование и вы знакомы с фреймворками вида xUnit, тогда вам подойдёт unittest.
- Если нужен фреймворк, позволяющий создавать краткие и изящные тесты, реализующие сложную логику проверок, то pytest.
Post Scriptum
Тема контроля качества очень обширна. И даже к написанному мной коду очень легко придраться. Как минимум здесь отсутствует проверка на то, что вводимые данные обязательно должны быть целыми числами. Если ввести любое другое число или даже строку, она обязательно завершится с ошибкой.
Кстати, в этой программе я намеренно оставил ещё одну ошибку (на сей раз уже логическую), связанную с нахождением корня. Напишите в комментариях, с чем она может быть связана, и какой тест поможет её отловить
Источник: tproger.ru