Недавно я столкнулся с Cyclomatic Complexity, и я хотел бы попытаться понять это лучше.
Каковы некоторые практические примеры кодирования различных факторов, влияющих на вычисление сложности? В частности, для уравнения Википедии M = E − N + 2P
я хочу лучше понять, что означает каждый из следующих терминов:
- E = количество ребер графа
- N = количество узлов графа
- P = количество подключенных компонентов
Я подозреваю, что либо E, либо N может быть числом точек принятия решения (если, иначе, если, for, foreach и т. Д.) В блоке кода, но я не совсем уверен, что означает, что или что означает другое. Я также предполагаю, что P относится к вызовам функций и экземплярам классов, но нет четкого определения, которое я вижу. Если бы кто-то мог пролить немного света на несколько примеров кода, это помогло бы.
В качестве продолжения, цикломатическая сложность напрямую коррелирует с количеством модульных тестов, необходимых для 100% покрытия пути ? Например, показывает ли метод со сложностью 4, что для охвата этого метода необходимы 4 модульных теста?
Наконец, влияют ли регулярные выражения на цикломатическую сложность, и если да, то как?
источник
Ответы:
Что касается формулы: узлы представляют состояния, ребра представляют изменения состояний. В каждой программе операторы вносят изменения в состояние программы. Каждый последовательный оператор представлен ребром, а состояние программы после (или до ...) выполнения оператора является узлом.
Если у вас есть оператор ветвления (
if
например) - тогда у вас будет два выходных узла, потому что состояние может измениться двумя способами.Другой способ вычисления числа цикломатической сложности (CCN) - это подсчет количества «областей» в графе выполнения (где «независимая область» - это круг, не содержащий других кругов). В этом случае CCN будет числом независимых регионов плюс 1 (которое будет точно таким же, как и в предыдущей формуле).
CCN используется для покрытия ветвления или покрытия пути , которое является одинаковым. CCN равно количеству различных путей ветвления, теоретически возможных в однопоточном приложении (которые могут включать в себя ветки типа "
if x < 2 and x > 5 then
", но это должно быть воспринято хорошим компилятором как недоступный код). Необходимо иметь как минимум такое количество различных тестовых случаев (может быть больше, поскольку некоторые тестовые примеры могут повторять пути, покрытые предыдущими, но не менее, если предположить, что каждый случай охватывает один путь). Если вы не можете покрыть путь каким-либо возможным тестовым примером - вы нашли недоступный код (хотя вам нужно будет доказать себе, почему он недоступен, возможно,x < 2 and x > 5
где-то скрывается где- то вложенный ).Что касается регулярных выражений - они, конечно, влияют, как и любой другой кусок кода. Тем не менее, CCN конструкции regex, вероятно, слишком велик, чтобы охватить его в одном модульном тесте, и вы можете предположить, что механизм regex был протестирован, и игнорировать потенциал ветвления выражений для ваших потребностей тестирования (если только вы не тестируете свой двигатель regex, конечно).
источник
Некоторые замечания по этому поводу, которые я праздно пишу ...
В частности, для уравнения Википедии M = E - N + 2P
Это уравнение очень неправильно .
По какой-то причине МакКейб действительно использует его в своей первоначальной работе («Мера сложности», IEEE Transactions по программной инженерии, Vo .. SE-2, № 4, декабрь 1976 г.), но без обоснования и после фактического цитирования правильной формула на первой странице, которая
(Здесь элементы формулы были перемаркированы)
В частности, МакКейб ссылается на книгу C.Berge, Графики и Гиперграфы (сокращенно G & HG). Прямо из этой книги :
Определение (стр. 27 внизу G & HG):
Теорема (стр. 29 вверху G & HG) (не используется МакКейбом):
Цикл представляет собой последовательность вершин , начиная и заканчивая в одной вершине, с каждыми двумя последовательными вершинами в последовательности смежных друг с другом в графике.
Интуитивно понятно, что набор циклов является независимым, если ни один из циклов не может быть построен из других путем наложения обходов.
Теорема (середина страницы G & HG) (используется МакКейбом):
Схема является циклом, без повторений вершин и ребер , разрешенных.
Направленный граф называется сильно связным, если каждая вершина достижима из любой другой вершины, проходя через ребра в назначенном направлении.
Обратите внимание, что здесь мы перешли от неориентированных графов к сильно связным графам (которые ориентированы ... Берге не дает понять это полностью)
Теперь МакКейб применяет приведенную выше теорему, чтобы вывести простой способ вычисления «числа цикломатической сложности МакКейба» (CCN) следующим образом:
При наличии ориентированного графа, представляющего «топологию перехода» процедуры (граф потока команд), с обозначенной вершиной, представляющей уникальную точку входа, и обозначенной вершиной, представляющей уникальную точку выхода (возможно, необходимо построить «вершину точки выхода» добавив его в случае многократного возврата), создайте сильно связанный граф, добавив направленное ребро из вершины точки выхода в вершину точки входа, тем самым делая вершину точки входа достижимой из любой другой вершины.
Теперь МакКейб утверждает (довольно смущающе, я бы сказал), что цикломатическое число модифицированного графа потока команд «соответствует нашему интуитивному представлению о« минимальном числе путей »», и поэтому мы будем использовать это число в качестве меры сложности.
Круто, так:
Число цикломатической сложности модифицированного графа потока команд может быть определено путем подсчета «наименьших» цепей в неориентированном графе. Это не особенно трудно сделать человеком или машиной, но применение приведенной выше теоремы дает нам еще более простой способ ее определения:
V (G) = E - V + P
если не обращать внимания на направленность краев.
Во всех случаях мы просто рассматриваем одну процедуру, поэтому во всем графе есть только один связанный компонент, и так:
v (G) = е - v + 1.
В случае, если рассматривать исходный граф без добавленного ребра «от входа к входу» , получается просто:
ṽ (G) = ẽ - v + 2
как ẽ = e - 1
Давайте проиллюстрируем это на примере МакКейба из его статьи:
Здесь мы имеем:
Формула для цикломатического числа гласит:
V (G) = E - V + P
что дает 5 = 10 - 6 + 1 и так правильно!
«Число цикломатической сложности МакКейба», приведенное в его статье,
5 = 9 - 6 + 2 (дальнейшие объяснения в статье не приведены)
что оказывается правильным (это дает v (G)), но по неправильным причинам, то есть мы используем:
ṽ (G) = ẽ - v + 2
и, таким образом, ṽ (G) = v (G) ... фу!
Но хороша ли эта мера?
В двух словах: не очень
for
циклы иwhile
циклы обрабатываются одинаково (обратите внимание, что в C можно злоупотреблятьfor
выражением awhile
по-другому; здесь я говорю о строгомfor (int i=0;i<const_val;i++)
цикле). Из теоретической информатики мы знаем, что эти две конструкции дают совершенно разные вычислительные мощности: примитивно-рекурсивные функции, если вы только оснащеныfor
, частично µ-рекурсивные функции, если вы оснащеныwhile
.источник
Да в принципе. Также неплохо использовать цикломатическую сложность в качестве индикатора того, когда следует проводить рефакторинг. По моему опыту, тестируемость и возможность многократного использования значительно возрастают при более низкой CC (хотя вы должны быть практичными - не подвергайте чрезмерному рефакторингу, а некоторые методы будут иметь высокую CC из-за своей природы - не всегда имеет смысл пытаться заставить его опустить).
Да, если вы хотите быть точным, хотя большинство инструментов анализа кода, похоже, не учитывают их таким образом. Регулярные выражения - это просто конечные автоматы, поэтому я предполагаю, что их CC можно рассчитать по графику FSM, но это будет довольно большое число.
источник