Индекс PHPUnit и C.R.A.P

Я использую php undercontrol, и браузер кода сообщает о некоторой ошибке индекса CRAP для каждого набора/геттера, то есть кода, такого как

public function getFoo()
{
    return $this->_foo;
}

Геттер/сеттер покрываются модульным тестированием, сложность отсутствует, так как нет if/for/switch/foreach. так почему я получаю индекс CRAP 1 для этого кода???

PS: автоответчик может быть вызван тем, что сложность - это не что-то другое, но моя основная проблема заключается в том, что каждый getter/setter генерирует предупреждение из-за индекса CRAP, так что есть все равно, чтобы рассказать покрытие phpunit/php, чтобы CRAP равнялся 0 для функции с индексом сложности 0.

Ответ 1

Минимальная оценка CRAP равна 1, а не 0. Это связано с тем, что алгоритм CRAP равен

CRAP(m) = comp(m)^2 * (1 – cov(m)/100)^3 + comp(m)

а значение минимальной циклической сложности (comp) для функции равно единице. Таким образом, проблема не в phpunit, но все, что помещает CRAP 1 в качестве проблемы.

В общем, вы хотите установить порог CRAP где-то около 5, где-нибудь ниже, и вы можете просто использовать простой показатель покрытия кода (и стрелять на 100%), поскольку коэффициент сложности едва весит. CRAP of >= 30 означает, что никакое количество тестов не может сделать ваш метод не дерьмовым.

Цикломатическая сложность может вообще (но есть более одного определения) вычисляться вручную как:

  • добавить 1 пункт для вызова функции
  • добавить 1 балл для каждого цикла
  • добавить 1 балл для каждой ветки

Ответ 2

Это действительно предупреждение? Как правило, порог для предупреждений устанавливается намного выше 1 (возможно, около 30). Существует хорошая публикация SO здесь, которая показывает, как рассчитывается число. Кажется, в моей настройке phpunit для CRAP 30 существует несколько жестко заданных значений.

Согласно Alberto Savoia, создатель индекса CRAP:

"Индекс C.R.A.P. (Change Risk Analysis and Prediction) предназначен для анализа и прогнозирования количества усилий, боли и времени, необходимых для поддержания существующего кода."

Минимальный номер CRAP будет циклической сложностью для кода со 100% охватом. Идея состоит в том, что изменения в сложном коде с большей вероятностью создают проблемы, чем изменения в простом коде.

Ответ 3

CRAP это только показатель. Само по себе это так же полезно, как "Как долго кусок строки?" кроме того, что это вопрос с неопределенным ответом, это также ответ с неопределенным вопросом.

Если вы знаете, что он измеряет, вы можете использовать его в качестве основного показателя сложности. Чтобы использовать его больше, вам нужно немало опыта для сравнения реализаций. После этого вы в идеале хотите сравнить реализации одного и того же. После этого вам необходимо тщательно знать о тестируемом коде, и, скорее всего, если вы это сделаете, у вас будет лучшее понимание, чем в баллах CRAP.

Чем оно выше, тем выше вероятность его улучшения по нескольким направлениям, таким как тестируемость (включая эффективность) и точки изменения. Тем не менее, только когда баллы более 8000 или 9000, вероятность того, что что-то станет абсолютным CRAP, начинает приближаться к определенности. Что-то столь же простое, как обработка узлов из разобранного XML-документа для функции, которая не может быть решена каким-либо решительным образом, может легко привести к сотням сложностей, в то же время будучи совершенно в порядке.

Это немного похоже на тратить деньги. Для определенной цели вам, возможно, придется потратить минимальную сумму. Это может быть миллион или тысяча, но независимо от цели мы склонны считать, что чем выше расходы, тем выше вероятность того, что они будут чрезмерными. Но, возможно, он должен быть высоким, возможно, вы покупаете яхту. Наивное сведение чисел не просто делает ту же ошибку в другом направлении, но действительно опасно. 71 человек был сожжен до смерти или задушен в башне Гренфелл из-за этой катастрофической ошибки в мышлении, полагая, что что-то может быть лучше всего достигнуто, если исходить только из числа.

Не следует предполагать, что сокращение CRAP улучшает тестируемость или ремонтопригодность. Довольно часто высокий CRAP просто сообщает о степени обязательной сложности. Вы можете технически уменьшить CRAP, играя числа, одновременно уменьшая тестируемость, ремонтопригодность и удобочитаемость. Вы можете улучшить эти вещи, только улучшив их. CRAP даже не является надежной мерой улучшения. Иногда CRAP может выйти из строя после улучшения. Иногда это может пойти вверх. Проблема с метрическими играми заключается в том, что люди часто просто смещают проблему или скрывают измеряемую вещь как показатель сложности.

Типичным примером является использование карты вместо переключателя или оператора if. Я склонен делать это самому религиозно. Однако мы забываем, что мы вытесняем сложность. В этом случае мы можем, у нас есть библиотека с утилитой карты, которая поддерживается и на которую можно положиться. Если вы включите эту функцию карты по сравнению с несколькими операторами if, общая мера сложности будет выше. Если в вашем распоряжении нет такой утилиты, вам нужно быть очень осторожным, как вы собираетесь уменьшить сложность. Например, если вы попытаетесь устранить операторы if и циклы for в целом, я желаю вам удачи в этом.

Цикломатическая сложность имеет тенденцию отражаться довольно хорошо, если скорость испытаний может быть улучшена. Это тривиально относится к таким случаям, как наличие двух операторов if в функции. Если состояние, на которое полагается второе, отличается в зависимости от того, совпадает первое или нет, то вы должны запустить первое избыточно (4 раза вместо 2).

Это также дает некоторое представление о том, сколько тестов вам действительно нужно с точки зрения параметров времени функции. Если у вас есть функция с одним логическим параметром и одним оператором if, основанным на ней, то вам нужно как минимум два теста, чтобы получить хотя бы полное покрытие кода. Два логических параметра и два оператора if, а затем четыре или три в зависимости от того, являются ли if вложенными. Количество комбинаций, которые вам, возможно, понадобится проверить, увеличивается на две степени только для каждой, если ее добавить после if в худшем случае.

Это может создать конфликт, заставляя вас преждевременно фрагментировать код, поскольку во время выполнения у вас может никогда не возникнуть такой проблемы. Как правило, вам не следует беспокоиться об этом до тех пор, пока тесты не начнут потреблять много ресурсов или фактически не станут непропорционально громоздкими. В этом случае вы не полагались бы на CRAP, но понимали бы выполнение кода и тесты.

CRAP может ошибиться, так как делает довольно наивное предположение о сложности. Вы приближаетесь к пессимистической или худшей оценке. Я смотрю на фрагмент кода, который получил высокий CRAP, но не может различить наличие if($constantInScope)etc;etc;if($constantInScope)etc; или if($varA)etc;etc;if($varB)etc;.

Если одна функция действительно имеет сотни, тысячи или миллионы возможных результатов с точки зрения пути выполнения, то это, вероятно, хороший признак того, что существуют проблемы с ее тестированием. это может быть неизбежным. И наоборот, это может быть гораздо проще проверить, чем указано. Ограниченная способность измерять цикломатическую сложность может быть причиной того, что CRAP также включает в себя противовес покрытия. Если вы проверили его и получили большой охват, то, вероятно, его не так сложно протестировать, как думала цикломатическая сложность, особенно учитывая, что можно сделать невозможным полное тестирование с точки зрения путей выполнения только по необходимости, хотя и очень редко.

Простой пример того, почему CRAP бесполезен, разверните ваши циклы и замените операторы if математическими утверждениями и т.п., чтобы уменьшить CRAP.