Когда вы программист-самоучка, очень легко упустить из виду правильный способ делать что-то. Я никогда не думал о модульном тестировании как о чем-то, пока не прошел формальный курс по питону. Курс был посвящен тому, чтобы делать то, что вы делаете в рабочей среде.
Идея состоит в том, что каждую строку кода, которую вы пишете, следует тестировать, чтобы избежать неожиданностей в последнюю минуту. Это также помогает в том смысле, что вам не нужно останавливать рабочий продукт для тестирования кода, но если у вас настроено тестирование, вы можете запускать тесты, и если возникнут какие-либо ошибки, вы будете знать, какая часть кода не работает ( констатируя очевидное, очевидно). Покрытие - это инструмент, который пригодится при тестировании, поскольку он генерирует отчет и дает вам процент вашего кода, который вы покрыли тестированием. Покрытие можно использовать вместе с unittest, pytest и даже с nodetest (но я с этим не знаком). Мы кратко рассмотрим, как использовать покрытие.
Если вам просто нужно краткое базовое руководство, вы можете пропустить эту статью до конца.
Исходный код:
Вы можете посмотреть репозиторий на github.
У нас есть довольно простой код на Python, есть функция, которая при задании имени распечатает инструкцию hello с именем, но если нет ввода, она распечатает инструкцию hello с незнакомцем вместо имени. У нас также есть некоторый код, который запрашивает ввод данных пользователем, а затем передает эти вводимые данные функции для вывода оператора hello.
#tutorial.py def say_hello(name=None): if name != "": print("Hello", name) else: print("Hello Stranger") if __name__ == "__main__": say_hello(input("What's your name? "))
Используя unittest, у нас есть другой файл, test_tutorial.py, который проверяет функцию say_hello, но передает «test» и проверяет, выводит ли он «Hello test» или нет.
#test_tutorial.py from tutorial import say_hello from unittest import TestCase from io import StringIO from unittest.mock import patch class PrintingTest(TestCase): def test_say_hello(self): name = 'test' expected_output = 'Hello {}\n'.format(name) with patch('sys.stdout', new=StringIO()) as fake_out: say_hello(name) self.assertEqual(fake_out.getvalue(), expected_output)
Теперь мы рассмотрим, как мы можем использовать покрытие для создания отчета о том, сколько кода покрыто тестированием в очень простом коде.
Установка
Как и любую другую библиотеку Python, Coverage можно установить с помощью:
pip install coverage
Если вы используете дистрибутив anaconda, вы можете использовать:
conda install coverage
Вы можете проверить установку покрытия, проверив версию:
coverage –version
Использование покрытия
Идея состоит в том, чтобы использовать его вместе с вашим тестовым раннером. Это довольно просто через командную строку. Мы можем рассмотреть несколько примеров.
Pytest: если вы используете pytest, вы можете добавить покрытие -m перед командой. Так:
pytest arg1 arg2 arg3
Это станет:
coverage run -m pytest arg1 arg2 arg3
Unittest: Лично я больше привык к модульному тестированию, и использовать покрытие с помощью unittest довольно просто. Все, что вам нужно сделать, это заменить python -m на покрытие run -m. так:
python -m unittest test_code.py
Это станет:
coverage run -m unittest test_code.py
Покрытие будет запускать тестирование и собирать данные. Чтобы просмотреть его как отчет, введите:
coverage report
Выполнение всех шагов в нашем коде выглядит следующим образом:
>coverage run -m unittest test_tutorial.py . — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — Ran 1 test in 0.001s OK >coverage report Name Stmts Miss Cover — — — — — — — — — — — — — — — — — — — — — — — — — test_tutorial.py 11 0 100% tutorial.py 6 2 67% — — — — — — — — — — — — — — — — — — — — — — — — — TOTAL 17 2 88%
Вы спросите, почему покрытие составляет 88%. Если мы еще раз рассмотрим tutorial.py, можно заметить, что у оператора if есть еще одна ветвь, что и должно происходить в случае «else». Это также должно быть проверено нашими тестами. Поэтому мы можем написать другой метод в классе тестирования, чтобы охватить эту ветвь.
#test_tutorial.py class PrintingTest(TestCase): ......... def test_say_hello_noname(self): name = '' expected_output = 'Hello Stranger\n' with patch('sys.stdout', new=StringIO()) as fake_out: say_hello(name) self.assertEqual(fake_out.getvalue(), expected_output)
Этот второй тест передает пустую строку и проверяет, является ли вывод «Hello Stranger». Текущее покрытие теперь создает следующий отчет:
>coverage run -m unittest test_tutorial.py .. — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — Ran 2 tests in 0.001s OK >coverage report Name Stmts Miss Cover — — — — — — — — — — — — — — — — — — — — — — — — — — — test_tutorial.py 17 0 100% tutorial.py 6 1 83% — — — — — — — — — — — — — — — — — — — — — — — — — — — TOTAL 23 1 96%
Определенно некоторое улучшение охвата, однако вы можете видеть, что это еще не 100%. Причина в том, что фрагмент кода в конце tutorial.py, который запрашивает ввод данных пользователем и вызывает функцию, не тестируется. Но что, если я не хочу покрывать это, а также не хочу, чтобы Покрытие включало это в отчет о покрытии тестами?
Исключение кода из покрытия: Это очень просто сделать, просто добавьте «# pragma: no cover».
........ if __name__ == "__main__": say_hello(input("What's your name? ")) # pragma: no cover
Давайте запустим наше тестирование и еще раз сгенерируем отчет о покрытии.
........... Name Stmts Miss Cover — — — — — — — — — — — — — — — — — — —— — — — — test_tutorial.py 17 0 100% tutorial.py 5 0 100% — — — — — — — — — — — — — — — — — — — — — — — — TOTAL 22 0 100%
Теперь это еще не все! Если вам нужен подробный отчет с улучшенным интерфейсом, который предоставляет вам построчную информацию, введите:
coverage html
Это сгенерирует графический интерфейс в виде веб-страницы. Обычно он сохраняется в папке с именем «htmlcov» в папке проекта, и для просмотра откройте файл index.html. Я призываю вас сделать это, отчет очень подробный, в нем отображается код, который покрыт, а код - нет.
Краткое пособие:
Installation: $pip install coverage If you are using anaconda distribution, you can use: $conda install coverage You can verify your Coverage installation by checking the version: $coverage –version Using Coverage Pytest $coverage run -m pytest arg1 arg2 arg3 Unittest $coverage -m unittest test_code Generating report $coverage report Generating HTML report $coverage html Excluding code from coverage Add a comment after the line "# pragma: no cover"
Статьи, которые я нашел очень полезными и были моим основным источником информации для этого урока:
Https://www.geeksforgeeks.org/python-testing-output-to-stdout/