Как найти код, который никогда не выполнялся в cover.py, несмотря на 100% отчет о покрытии - PullRequest
0 голосов
/ 28 февраля 2020

Рассмотрим следующий код:

import math

def dumb_sqrt(x):
    result = math.sqrt(x) if x >= 0 else math.sqrt(-x)*j
    return result


def test_dumb_sqrt():
    assert dumb_sqrt(9.) == 3.

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

$ pip install pytest pytest-cov
$ pytest test_thing.py --cov=test_thing --cov-report=html --cov-branch

В отчете о покрытии будут рассмотрены все линии, покрытые на 100%, даже с включенным покрытием ветвлений :

inline

Однако, в этом коде есть ошибка , и те из вас, у кого есть острый глаз, возможно, уже видели это. Если он когда-нибудь go попадет в ветку "else", будет исключение:

NameError: global name 'j' is not defined

Исправить ошибку легко: измените неопределенное j имя в литерал 1j. Также легко добавить еще один тест, который выявит ошибку: assert dumb_sqrt(-9.) == 3j. Не то, о чем этот вопрос спрашивает. Я хочу знать, как найти участки кода, которые фактически никогда не выполнялись , несмотря на 100% -ный отчет о покрытии кода.

Использование условных выражений является одним из таких виновников, но в любом случае встречаются похожие случаи, когда Python может привести к короткому замыканию оценки (x or y, x and y - другие примеры).

Предпочтительно, строка 4 выше может быть окрашена в отчет в желтый цвет, аналогично тому, как «если» Если бы в строке не использовалось условное выражение:

long

Поддерживает ли coverage.py такую ​​функцию? Если да, то как вы можете включить «встроенное покрытие филиалов» в своих отчетах за cov? Если нет, есть ли другие подходы для определения «скрытого» кода, который фактически никогда не выполнялся вашим набором тестов?

Ответы [ 2 ]

2 голосов
/ 28 февраля 2020

Нет, cover.py не обрабатывает условные переходы внутри выражения. Это влияет не только на условное выражение Python, также будет затронуто использование and или or:

# pretending, for the sake of illustration, that x will never be 0
result = x >= 0 and math.sqrt(x) or math.sqrt(-x)*j

Нед Бэтчелдер, сопровождающий coverage.py, называет это скрытое условие , в статье от 2007 года, посвященной этому и другим случаям coverage.py не может обработать .

Эта проблема распространяется и на операторы if! Возьмем, к примеру:

if condition_a and (condition_b or condtion_c):
    do_foo()
else:
    do_bar()

Если condition_b всегда верно, когда condition_a истинно, вы никогда не найдете опечатку в condtion_c, даже если вы полагаетесь исключительно на converage.py, потому что нет поддержки условного покрытия (не говоря уже о более продвинутых понятиях, таких как измененное покрытие условия / решения и покрытие нескольких условий .

One Препятствие для поддержки условного покрытия является техническим: coverage.py сильно зависит от встроенной поддержки трассировки Python , но до недавнего времени это позволяло отслеживать только выполнение по строке . Нед на самом деле исследовал обходные пути для этой проблемы .

Не то, чтобы это остановило другой проект, инструментальный от предоставления покрытия условий / решений в любом случае Этот проект использовал переписывание AST и ловушку импорта, чтобы добавить дополнительный байт-код, который позволил бы ему отслеживать результаты отдельных условий и, таким образом, дать вам представление о «таблице истинности» выражений. Вернемся к такому подходу: он очень быстрый agile и часто требует обновления для новых Python выпусков В результате проект разорвался с Python 3.4 и не был исправлен.

Тем не менее, Python 3.7 добавил поддержку трассировки на уровне кода операции , позволяя трассировщику анализировать влияние каждого отдельного байт-кода без необходимости взлома AST. И так как cover.py 5.0 достиг стабильного состояния, кажется, что проект рассматривает возможность добавления поддержки покрытия условий , с возможными спонсорами для поддержки разработки.

Так что ваши варианты сейчас :

  • Запустите ваш код в Python 3.3 и используйте инструментал
  • Восстановите инструмент для запуска на более поздних Python версиях
  • Подождите, чтобы покрытие. добавить условие покрытия
  • Помогите написать функцию для cover.py
  • Соберите свою собственную версию подхода инструментала, используя режим трассировки Python 3.7 или новее 'opcode'.
0 голосов
/ 28 февраля 2020

Не используйте res1, если cond else res2 - это нужно рассматривать как одно утверждение. Если вы напишите это как if / else, я думаю, что cover.py сделает свою работу лучше.

И рассмотрите возможность использования чего-то вроде pylint или хотя бы pyflakes. Я полагаю, что они бы автоматически обнаружили проблему.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...