Различия между версиями 1 и 14 (по 13 версиям)
Версия 1 от 2021-04-22 08:44:09
Размер: 15374
Редактор: FrBrGeorge
Комментарий:
Версия 14 от 2021-04-25 22:26:08
Размер: 18976
Редактор: FrBrGeorge
Комментарий:
Удаления помечены так. Добавления помечены так.
Строка 14: Строка 14:
   * acceptacne — потребительские/рыночные/эксплутационные/… свойства продукта
 * Дисциплина:
   * acceptacne — потребительские/рыночные/эксплуатационные/… свойства продукта
 * Тест регрессий
 * Покрытие
  * Функции / классы
  * Строчки кода
  * Различные execution path (?)
  * Различные execution path с критически различными наборами данных (??)
 * Test-driven development:
Строка 28: Строка 34:
  * '''дефекту''' (багу) в программе, который в свою очередь может    * '''дефекту''' (багу) в программе, который в свою очередь может
Строка 33: Строка 39:
'''TODO''' исправить под использование `pipenv`
Строка 36: Строка 41:
==== Пример использования doctest ====
 * [[LecturesCMC/PythonDevelopment2020/09_Testing#Doctest|В лекциях прошлого года]]
 * [[https://git.sr.ht/~frbrgeorge/MooTest/log|В репозитории с примерами к этой лекции]]
Поэтому только кратко:
Строка 37: Строка 46:
 {{{#!python
def moo(oos=2, end=""):
    '''Издать мычание длиной oos с end в конце'''
    return "M"+"o"*oos+end
 }}}
 1. Тестируем вручную:
 {{{#!highlight pycon
$ python3 -i Moo.py
>>> moo()
'Moo'
>>> moo(4)
'Moooo'
>>> moo(0)
'M'
>>> moo(end='!')
'Moo!'
>>> moo(0,'?')
'M?'
}}}
 1. Добавляем тесты в docstring:
 {{{#!pytnon
def moo(oos=2, end=""):
    '''Издать мычание длиной oos с end в конце
    Оба параметра необязательны:
>>> moo()
'Moo'

    Первый задаёт количество букв 'o' в слове 'Moo'
>>> moo(4)
'Mooooo'

    Букв 'o' может и не быть
>>> moo(0)
'M'

    Второй задаёт символ после всех 'o'
    (по умолчанию — ничего)
>>> moo(end='!')
'Moo!'
>>> moo(0,'?')
'M?'

    '''
    return "M"+"o"*oos+end
}}}
 1. Тестирование:
 {{{#!highlight console
$ python3 -m doctest Moo.py
**********************************************************************
File "/home/george/src/moo/Moo.py", line 13, in Moo.moo
Failed example:
    moo(4)
Expected:
    'Mooooo'
Got:
    'Moooo'
**********************************************************************
1 items had failures:
   1 of 5 in Moo.moo
***Test Failed*** 1 failures.
}}}
 1. Отчёт (с успешными тестами):
 {{{#!highlight console
$ python3 -m doctest -v Moo.py
Trying:
    moo()
Expecting:
    'Moo'
ok
Trying:
    moo(4)
Expecting:
    'Mooooo'
**********************************************************************
File "/home/george/src/moo/Moo.py", line 13, in Moo.moo
Failed example:
    moo(4)
Expected:
    'Mooooo'
Got:
    'Moooo'
Trying:
    moo(0)
Expecting:
    'M'
ok
Trying:
    moo(end='!')
Expecting:
    'Moo!'
ok
Trying:
    moo(0,'?')
Expecting:
    'M?'
ok
1 items had no tests:
    Moo
**********************************************************************
1 items had failures:
   1 of 5 in Moo.moo
5 tests in 2 items.
4 passed and 1 failed.
***Test Failed*** 1 failures.
}}}

Тестирование исключений:
{{{#!highlight pycon
$ python -im Moo
>>> moo("QQ")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/george/src/tests/Moo.py", line 33, in moo
    return "M"+"o"*moos+end
TypeError: can't multiply sequence by non-int of type 'str'
>>>
}}}

Как обычно, добавим ''просто весь вывод''!
{{{#!python
def moo(oos=2, end=""):
    '''Издать мычание длиной oos с end в конце
...

Здесь должно быть исключение:
>>> moo("QQ")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/george/src/tests/Moo.py", line 33, in moo
    return "M"+"o"*moos+end
TypeError: can't multiply sequence by non-int of type 'str'
'''
}}}

Вообще говоря, важны только три строчки, остальные можно выкинуть
{{{
...
Здесь должно быть исключение:
>>> moo("QQ")
Traceback (most recent call last):
TypeError: can't multiply sequence by non-int of type 'str'
...
}}}

Тесты должны пройти!

[[py3doc:/doctest.html#simple-usage-checking-examples-in-a-text-file|Перенос тестов во внешний файл]]
 
Пишем файл ( <!> можно в `.rst`, для Sphinx), например, `exttest.rst`:
{{{#!highlight rst
External test
=============

Using Moo
---------

Start from importing `Moo` module:

        >>> import Moo

Then call `moo`:
        >>> Moo.moo(5)
        'Mooooo'
}}}

К нему запускалку тестов:
{{{#!highlight python
import doctest
doctest.testfile("exttest.rst")
}}}

И запускаем её (ключи как у модуля `pytest`):
{{{#!console
$ python3 exttest.py -v
Trying:
    import Moo
Expecting nothing
ok
Trying:
    Moo.moo(5)
Expecting:
    'Mooooo'
ok
1 items passed all tests:
   2 tests in exttest.rst
2 tests in 1 items.
2 passed and 0 failed.
Test passed.

}}}
 1. Тестируем вручную из командной строки
 1. Добавляем диалог as is в docstring:
 1. Тестирование: `python3 -m doctest Moo.py`
 1. Отчёт (с успешными тестами): `python3 -m doctest -v Moo.py`
 1. Тестирование исключений
  * Вообще говоря, важны только три строчки (сама команда, первая строка и сообщение с исключением), остальные можно выкинуть
 1. [[py3doc:doctest#simple-usage-checking-examples-in-a-text-file|Перенос тестов во внешний текстовый файл]],
  * например, в `.rst`
  * этот файл отлично включается в Sphinx-документацию
  * Запуск `python3 exttest.py -v`:
   {{{#!highlight python
   import doctest
   doctest.testfile("exttest.rst")
   }}}
Строка 229: Строка 62:
 Fixture:: Подготовка компонента к тесту: не все функции можно оттестировать сходу, иногда надо   Fixture:: Подготовка компонента к тесту: не все функции можно оттестировать сходу, иногда надо
Строка 235: Строка 68:
 SubCase:: Множественное тестирование (например, тестирование в цикле обреботки нескольких наборов параметров)
Строка 237: Строка 71:
==== Pydoc ==== === Модуль unittest ===
Принципы [[py3doc:unittest]]:
 * Тестирующая функция вызывает проверочный метод `assertЧтоТоТам(какие-то, параметры)` или проверяет в контекстном менеджере, что выпале нужное исключение / warning
 * Case — это класс, в нём несколько атомарных тестирующих функций
 * Suite — это объект, в который можно добавлять Case и другие Suite
 * Runner — заводится автоматом, но можно задать вручную с выбором Suite
 * Fixture — два метода в Case (`.SetUp()` и `.TearDown()`)
Возможности:
 * Сбор тестов (discovery)
 * Пропуск тестов по условию
 * Ожидаемый сбой
 * Подтесты
 * Обработка сигналов
==== Примеры использования ====
 * [[https://git.sr.ht/~frbrgeorge/MooTest/log|В репозитории примеров к лекции]]
 * [[https://tirinox.ru/unit-test-python/|В методичке]]

=== Квазиобъекты (mock) ===
Основные понятия:
 Квазиобъект (mock pbject, mocker):: объект, создаваемый в процессе тестирования ''вместо'' «настоящего» объекта
  * Как правило, умеет всё сразу (его можно вызывать с любыми параметрами, обращаться к любым полям внутри него и т. п.)
  * Умеет отчитываться (такой-то метод был вызван так-то)
  * Свойства объекта и его полей (которые создаются автоматически как такие же квазиобъекты) — настраиваемые. Например, можно задать возвращаемые значения, значение некоторых полей, вызываемые исключения и т. п.
 Патч (patch или monkey patch):: подмена на время теста полей реального объекта на квазиобъекты
  * Нередко обладает свойством самоудаляться по окончании теста. Например, патч оформляется как [[py3doc:stdtypes.html#typecontextmanager|контекстный менеджер]]
 Индикатор (sentinel):: уникальный объект, который передаётся в тестируемую подсистему, и по окончании теста должен продолжать существовать где-то в её недрах
  * Если индикатор в процессе тестирования удаляется, тест не пройден

[[py3doc:unittest.mock]]
 * [[py3doc:unittest.mock-examples|примеры]]

Небольшой пример использования квазиобъектов в [[https://git.sr.ht/~frbrgeorge/GradeProject2021|Модельном семестровом проекте]]
 * (для понимания стоит открыть исходный код проекта)
 {{{#!python
class TestDateTime(unittest.TestCase):
    date_init = "%c"

    def setUp(self):
        self.view = MagicMock()
        self.view.sFormat.get = MagicMock(return_value=self.date_init)
        self.view.sStart.get = MagicMock(return_value="1")
        self.view.sCaltype.get = MagicMock(return_value=3)
        self.view.Date, self.view.Calendar = {}, {}
        self.model = AppModel(self.view)
        self.control = AppControl(self.model)

    def test_0_init(self):
        assert self.model.view is self.view
        assert self.control.model is self.model

    def test_1_call(self):
        self.model(self.control)
        self.view.assert_called_once_with(self.control)
        self.view.sFormat.get.assert_called_once()
        self.view.sStart.get.assert_called_once()
        self.view.sCaltype.get.assert_called_once()
        self.assertEqual(self.view.Date["text"], time.strftime(self.date_init))
        res = subprocess.run(["cal", "-3"], capture_output=True)
        self.assertEqual(self.view.Calendar["text"], res.stdout.decode())
 }}}
  * `setUp()` — подготовка фикстуры
   * Вместо View используется квазиобъект (избавляемся от tkinter)
   * Вместо управляющих переменных — квазиобьекты с заданным поведением метода `.gt()`
   * Вместо «словарного» интерфейса по изменению настроек виджетов `tkinter` — настоящие словари, где будет оседать результат тестирования
   * Остальные объекты настоящие
  * `test_0_init()` — примитивный тест
  * `test_1_call()` — тест активации всего комплекса Model→View→Control (с квази Vew)
   * должен быть активирован View (с параметром Control)
   * должны быть опрошены управляющие переменные (в процессе инициализации)
   * поля в View должны быть инициализированы результатами работы `cal` и `strftime()`
Строка 239: Строка 143:
[[py3doc:unittest]]
 * [[https://tirinox.ru/unit-test-python/|методичка]]

=== Тестовое покрытие ===
(''[[https://pypi.org/search/?q=coverage|тысячи их]]'')

Модуль [[pypi:coverage]]
 * поддержка unittest, pytest (и слегка старого уже nose)
 * выборочное покрытие
 * маркировка ветвлений
 * подпроцессы
 * журналирование контекста

==== Пример ====
[[https://git.sr.ht/~frbrgeorge/MooTest/log|В репозитории примеров к лекции]]
 * Набор тестов
 * Исключение того, что тестировать не надо
=== Модуль pytest ===
Строка 246: Строка 164:
  * Атомарные фикстуры (пеернаправление в/в, временные изменения классов и т. п.)   * Атомарные фикстуры (перенаправление в/в, временные изменения классов и т. п.)
Строка 248: Строка 166:
Строка 270: Строка 187:
(demo-pudb) [george@inspiron pudb]$ pip install -r requirements.dev.txt  (demo-pudb) [george@inspiron pudb]$ pip install -r requirements.dev.txt
Строка 286: Строка 203:
(demo-pudb) [george@inspiron pudb]$ pytest                              (demo-pudb) [george@inspiron pudb]$ pytest
Строка 291: Строка 208:
collected 16 items  collected 16 items
Строка 312: Строка 229:
collected 16 items  collected 16 items
Строка 325: Строка 242:
(demo-pudb) [george@inspiron pudb]$ pytest --cov=build/lib/pudb   (demo-pudb) [george@inspiron pudb]$ pytest --cov=build/lib/pudb
Строка 330: Строка 247:
collected 16 items  collected 16 items

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

много теории без питона тут

  • Место тестирования в жизненном цикле программного продукта
    • Собирается — значит, работает!
    • + Проверяется анализатором кода, и потыкано end-user тестерами согласно тест-плану
    • + Автоматическая проверка работоспособности там, где это возможно
    • + Измерение тестового покрытия (coverage)
  • Ручное, автоматизированное, автоматическое
  • Виды тестирования

    • Уровни

      • unit — спецификация компонентов (функций/классов и т. п.)
      • integration — спецификация интерфейсов между компонентами

      • system — спецификация конечного продукта
      • acceptacne — потребительские/рыночные/эксплуатационные/… свойства продукта
  • Тест регрессий
  • Покрытие
    • Функции / классы
    • Строчки кода
    • Различные execution path (?)
    • Различные execution path с критически различными наборами данных (??)
  • Test-driven development:
    • Сначала весь код, потом некоторые тесты (когда это можно?)
    • Каждая новая фича сопровождается тестом
    • Разработка_через_тестирование (TDD)

      1. сначала пишется тест и заглушка
      2. сам код падает (иначе бесполезен)

      3. под тест пишется код
      4. код не падает
      5. код изменяется и всё равно не падает

  • green test trap: Тестирование может доказать наличие дефектов, но не их отсутствие
  • red test trap: Не всякие проваленные тесты означают дефекты. Могут означать пробел в требованиях, в том числе нефункциональных
  • Полезные ≠ друг другу термины:
    • ошибка программиста при написании программы может привести к

    • дефекту (багу) в программе, который в свою очередь может

    • проявиться (или не проявиться) в виде программного сбоя

  • Стоимость исправления дефекта возрастает пропорционально его «возрасту»
  • Непрерывная интеграция

Модульное тестирование в Python

Doctest

doctest: тест = диалог с python-интерпретатором

Пример использования doctest

Поэтому только кратко:

  1. Модуль
  2. Тестируем вручную из командной строки
  3. Добавляем диалог as is в docstring:
  4. Тестирование: python3 -m doctest Moo.py

  5. Отчёт (с успешными тестами): python3 -m doctest -v Moo.py

  6. Тестирование исключений
    • Вообще говоря, важны только три строчки (сама команда, первая строка и сообщение с исключением), остальные можно выкинуть
  7. Перенос тестов во внешний текстовый файл,

    • например, в .rst

    • этот файл отлично включается в Sphinx-документацию
    • Запуск python3 exttest.py -v:

      •    1    import doctest
           2    doctest.testfile("exttest.rst")
        

«Серьёзные» фреймворки

Fixture
Подготовка компонента к тесту: не все функции можно оттестировать сходу, иногда надо
  1. сначала что-то создать, открыть, запустить, … (set-up)

  2. провести тест
  3. удалить, закрыть, остановить, … это что-то (tear-down) Такое одно что-то называет fixture

Case

Что именно тестируем. Подготавливаем окружение (fixtur-ы), что-то дёргаем, смотрим, подходит ли результат

SubCase
Множественное тестирование (например, тестирование в цикле обреботки нескольких наборов параметров)
Suite
Набор cases (на разные темы, разных больших частей, разных уровней и т. п.)
Runner
Запускалка тестов, обработчик отчётов и т. п.

Модуль unittest

Принципы unittest:

  • Тестирующая функция вызывает проверочный метод assertЧтоТоТам(какие-то, параметры) или проверяет в контекстном менеджере, что выпале нужное исключение / warning

  • Case — это класс, в нём несколько атомарных тестирующих функций
  • Suite — это объект, в который можно добавлять Case и другие Suite
  • Runner — заводится автоматом, но можно задать вручную с выбором Suite
  • Fixture — два метода в Case (.SetUp() и .TearDown())

Возможности:

  • Сбор тестов (discovery)
  • Пропуск тестов по условию
  • Ожидаемый сбой
  • Подтесты
  • Обработка сигналов

Примеры использования

Квазиобъекты (mock)

Основные понятия:

Квазиобъект (mock pbject, mocker)

объект, создаваемый в процессе тестирования вместо «настоящего» объекта

  • Как правило, умеет всё сразу (его можно вызывать с любыми параметрами, обращаться к любым полям внутри него и т. п.)
  • Умеет отчитываться (такой-то метод был вызван так-то)
  • Свойства объекта и его полей (которые создаются автоматически как такие же квазиобъекты) — настраиваемые. Например, можно задать возвращаемые значения, значение некоторых полей, вызываемые исключения и т. п.
Патч (patch или monkey patch)
подмена на время теста полей реального объекта на квазиобъекты
  • Нередко обладает свойством самоудаляться по окончании теста. Например, патч оформляется как контекстный менеджер

Индикатор (sentinel)
уникальный объект, который передаётся в тестируемую подсистему, и по окончании теста должен продолжать существовать где-то в её недрах
  • Если индикатор в процессе тестирования удаляется, тест не пройден

unittest.mock

Небольшой пример использования квазиобъектов в Модельном семестровом проекте

  • (для понимания стоит открыть исходный код проекта)
       1 class TestDateTime(unittest.TestCase):
       2     date_init = "%c"
       3 
       4     def setUp(self):
       5         self.view = MagicMock()
       6         self.view.sFormat.get = MagicMock(return_value=self.date_init)
       7         self.view.sStart.get = MagicMock(return_value="1")
       8         self.view.sCaltype.get = MagicMock(return_value=3)
       9         self.view.Date, self.view.Calendar = {}, {}
      10         self.model = AppModel(self.view)
      11         self.control = AppControl(self.model)
      12 
      13     def test_0_init(self):
      14         assert self.model.view is self.view
      15         assert self.control.model is self.model
      16 
      17     def test_1_call(self):
      18         self.model(self.control)
      19         self.view.assert_called_once_with(self.control)
      20         self.view.sFormat.get.assert_called_once()
      21         self.view.sStart.get.assert_called_once()
      22         self.view.sCaltype.get.assert_called_once()
      23         self.assertEqual(self.view.Date["text"], time.strftime(self.date_init))
      24         res = subprocess.run(["cal", "-3"], capture_output=True)
      25         self.assertEqual(self.view.Calendar["text"], res.stdout.decode())
    
    • setUp() — подготовка фикстуры

      • Вместо View используется квазиобъект (избавляемся от tkinter)
      • Вместо управляющих переменных — квазиобьекты с заданным поведением метода .gt()

      • Вместо «словарного» интерфейса по изменению настроек виджетов tkinter — настоящие словари, где будет оседать результат тестирования

      • Остальные объекты настоящие
    • test_0_init() — примитивный тест

    • test_1_call() — тест активации всего комплекса Model→View→Control (с квази Vew)

      • должен быть активирован View (с параметром Control)
      • должны быть опрошены управляющие переменные (в процессе инициализации)
      • поля в View должны быть инициализированы результатами работы cal и strftime()

TODO

Тестовое покрытие

(тысячи их)

Модуль coverage

  • поддержка unittest, pytest (и слегка старого уже nose)
  • выборочное покрытие
  • маркировка ветвлений
  • подпроцессы
  • журналирование контекста

Пример

В репозитории примеров к лекции

  • Набор тестов
  • Исключение того, что тестировать не надо

Модуль pytest

pytest

  • Сравнение с unittest:

    • Test discover «из коробки» — поиск тестов по имени файла/функции/класаа (есть в unittest)

    • Обычный assert для теста (вместо многих функций)

    • Атомарные фикстуры (перенаправление в/в, временные изменения классов и т. п.)
    • Множество дополнений

Пример использования pytest: pudb

PuDB — отладчик для питона

Соберём под него окружение:

   1 [george@inspiron src]$ python3 -m venv init demo-pudb
   2 [george@inspiron src]$ cd demo-pudb
   3 [george@inspiron demo-pudb]$ . ./bin/activate
   4 (demo-pudb) [george@inspiron demo-pudb]$ git clone https://github.com/inducer/pudb.git
   5 Cloning into 'pudb'...
   6 cd pudb
   7 (demo-pudb) [george@inspiron pudb]$  ls
   8 debug_me.py             example-theme.py  pudb                  setup.py
   9 doc                     LICENSE           README.rst            test
  10 example-shell.py        MANIFEST.in       requirements.dev.txt  try-the-debugger.sh
  11 example-stringifier.py  manual-tests      setup.cfg             upload_coverage.sh
  12 (demo-pudb) [george@inspiron pudb]$
  13 

Cоберём модуль:

   1 (demo-pudb) [george@inspiron pudb]$ pip install -r requirements.dev.txt
   2 Collecting codecov==2.0.5 (from -r requirements.dev.txt (line 1))
   3 . . .
   4 Successfully installed Pygments-2.2.0 argparse-1.4.0 . . .
   5 (demo-pudb) [george@inspiron pudb]$ python setup.py build
   6 running build
   7 . . .
   8 copying pudb/ui_tools.py -> build/lib/pudb
   9 (demo-pudb) [george@inspiron pudb]$
  10 
  • Все нужные зависимости указаны в файла requirements.dev.txt

  • Собранный модуль в чистом виде лежит в build/lib/pudb

  • Для запуска тестов надо сделать так, чтобы build/lib попал в PYTHONPATH, тогда модуль будет экспортирован оттуда

   1 (demo-pudb) [george@inspiron pudb]$ export PYTHONPATH=`pwd`/build/lib
   2 (demo-pudb) [george@inspiron pudb]$ pytest
   3 ========================= test session starts =========================
   4 platform linux -- Python 3.8.2, pytest-3.0.7, py-1.4.33, pluggy-0.4.0
   5 rootdir: /home/george/src/demo-pudb/pudb, inifile:
   6 plugins: mock-1.10.0, cov-2.4.0
   7 collected 16 items
   8 
   9 test/test_lowlevel.py ....
  10 test/test_make_canvas.py .....
  11 test/test_settings.py ..
  12 test/test_source_code_providers.py ....
  13 test/test_var_view.py .
  14 
  15 ====================== 16 passed in 0.22 seconds ======================
  16 
  • pytest сам нашёл, где лежат тесты

  • Можно сказать pytest -v для отчёта по каждому тесту в файле

В этом проекте используется два дополнения к pytest:

  • вместо полноценных фикстур — т. н. моккеры (mock):
       1 (demo-pudb) [george@inspiron pudb]$ pytest --fixtures-per-test
       2 =========================== test session starts ============================
       3 platform linux -- Python 3.8.2, pytest-3.0.7, py-1.4.33, pluggy-0.4.0
       4 rootdir: /home/george/src/demo-pudb/pudb, inifile:
       5 plugins: mock-1.10.0, cov-2.4.0
       6 collected 16 items
       7 
       8 ------------------ fixtures used by test_load_breakpoints ------------------
       9 ------------------------ (test/test_settings.py:10) ------------------------
      10 mocker
      11     return an object that has the same interface to the `mock` module, but
      12     takes care of automatically undoing all patches after each test method.
      13 pytestconfig
      14     the pytest config object with access to command line opts.
      15 . . .
      16 
    
  • Замер покрытия кода тестами cov

       1 (demo-pudb) [george@inspiron pudb]$ pytest --cov=build/lib/pudb
       2 =========================== test session starts =======================
       3 platform linux -- Python 3.8.2, pytest-3.0.7, py-1.4.33, pluggy-0.4.0
       4 rootdir: /home/george/src/demo-pudb/pudb, inifile:
       5 plugins: mock-1.10.0, cov-2.4.0
       6 collected 16 items
       7 
       8 test/test_lowlevel.py ....
       9 test/test_make_canvas.py .....
      10 test/test_settings.py ..
      11 test/test_source_code_providers.py ....
      12 test/test_var_view.py .
      13 
      14 ----------- coverage: platform linux, python 3.8.2-final-0 -----------
      15 Name                            Stmts   Miss Branch BrPart  Cover
      16 -----------------------------------------------------------------
      17 build/lib/pudb/__init__.py        194    159     26      4    18%
      18 build/lib/pudb/__main__.py          3      3      0      0     0%
      19 build/lib/pudb/b.py                14     14      2      0     0%
      20 build/lib/pudb/debugger.py       1386   1276    223      2     7%
      21 build/lib/pudb/ipython.py          31     31      8      0     0%
      22 build/lib/pudb/lowlevel.py        134     61     56      7    51%
      23 build/lib/pudb/py3compat.py        23     10      2      1    56%
      24 build/lib/pudb/remote.py          120    120     12      0     0%
      25 build/lib/pudb/run.py              27     27      0      0     0%
      26 build/lib/pudb/settings.py        378    302     88     13    22%
      27 build/lib/pudb/shell.py           137    137     12      0     0%
      28 build/lib/pudb/source_view.py     230    167     36      7    26%
      29 build/lib/pudb/theme.py           595    595      2      0     0%
      30 build/lib/pudb/ui_tools.py        222    159     66      0    25%
      31 build/lib/pudb/var_view.py        396    324     92      6    18%
      32 -----------------------------------------------------------------
      33 TOTAL                            3890   3385    625     40    13%
      34 ======================== 16 passed in 0.55 seconds ===================
      35 
    
  • Без указания --cov=build/lib/pudb ключ --cov посчитает покрытие всего запускаемого кода на python (включая все системные библиотеки:)

  • Вместо дополнения к pytest можно использовать отдельный модуль coverage

Д/З

  • Осознать, что нуждается в unit-тестировании
  • Оснастить код семестрового проекта unit-тестами (любой фреймворк)
    • Зафиксировать в документации, как их запускать
  • Подумать над тестированием UI
    • например, с помощью порождения событий tkinter

LecturesCMC/PythonDevelopment2021/10_Testing (последним исправлял пользователь FrBrGeorge 2021-04-28 19:22:23)