Когда вы программист-самоучка, очень легко упустить из виду правильный способ делать что-то. Я никогда не думал о модульном тестировании как о чем-то, пока не прошел формальный курс по питону. Курс был посвящен тому, чтобы делать то, что вы делаете в рабочей среде.

Идея состоит в том, что каждую строку кода, которую вы пишете, следует тестировать, чтобы избежать неожиданностей в последнюю минуту. Это также помогает в том смысле, что вам не нужно останавливать рабочий продукт для тестирования кода, но если у вас настроено тестирование, вы можете запускать тесты, и если возникнут какие-либо ошибки, вы будете знать, какая часть кода не работает ( констатируя очевидное, очевидно). Покрытие - это инструмент, который пригодится при тестировании, поскольку он генерирует отчет и дает вам процент вашего кода, который вы покрыли тестированием. Покрытие можно использовать вместе с 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/