Цикломатическая сложность справедливо снижается с помощью частных методов?
Использование частных методов для уменьшения CC путем рефакторинга некоторых точек принятия решения в отдельные методы уменьшает CC фактического метода и облегчает чтение, но не уменьшает усилия для получения полного охвата ветвления в тестировании.
Это оправдано? Какой у вас опыт?
6 ответов
Иногда, делая ваш код приложения менее сложным и более читаемым, в результате ваш тестовый код становится более сложным и менее читаемым. Однако это не причина не проводить рефакторинг. Читаемость рабочего кода важнее ваших тестов.
Если вы сделаете некоторые методы закрытыми для уменьшения CC и улучшения читабельности, вы можете использовать такую среду, как Mockito, чтобы все еще иметь возможность тестировать сами закрытые методы.
Хорошо, если вы все еще заинтересованы, есть эта статья, которую я записал на Cyclomatic Complexity
Разрезание вашего кода разными методами не уменьшит сложность, а только поможет улучшить организацию на местном уровне. Единственный реальный способ уменьшить CC - это действительно провести рефакторинг!
Вам понадобится такое же количество тестов, если вы извлекаете только методы. Посмотрите на ваши структуры данных, может быть, они вам не помогают? Рассмотрите сокращение в нескольких классах также, если это имеет смысл.
По моему опыту, это очень распространенная ситуация - если заставить ваш производственный код делать все правильно, то его сложнее тестировать
Другие примеры включают в себя сокрытие деталей реализации за интерфейсами, никогда не связываться с сущностями ORM для внешних вызывающих абонентов, предоставление определенных функций только через вызовы веб-служб... и, как правило, наличие API, который вы ожидаете в производственной среде, довольно часто ограничивает тестирование.
И да, больно возвращать ваше покрытие после существенного рефакторинга. Иногда это приводит к обращению вспять общего прогресса, когда функции, которые так сложно протестировать, не тестируются должным образом. Так что я в целом согласен с Фортегой, кроме самого последнего предложения. Не позволяйте вашим тестам гнить. Они вернутся, когда ты этого меньше всего захочешь.
Я не очень понимаю вашу проблему. Очевидно, что рефакторинг логики и точек принятия решения из большого метода в частные методы уменьшит цикломатическую сложность больших методов. Это своего рода точка извлечения методов - она делает большой метод короче и проще, поэтому, надеюсь, его легче понять и изменить.
Это не обман, это просто делает структуру вашей программы явной.
но не уменьшает усилия, чтобы получить полное покрытие ветви в тестировании.
Мне кажется, это не является следствием. Почему использование частных методов должно облегчить тестирование покрытия? Я никогда не видел, чтобы кто-то заявлял об этом. Возможно, вы что-то неправильно поняли?
Вы никогда не должны реорганизовывать свой код из-за ощущения, что улучшение чисел, которые выдает метрика, хорошо для вашего кода. Вместо того, чтобы идти за цифрами и необычными отчетами (я видел, что это сделано к сожалению), вы должны стремиться понять метрику и почему она используется в первую очередь.
Цикломатическая сложность - это простая математическая мера, которая только указывает, сколько разных путей может выполнить ваш код, если все его ветви пройдены. Так что да, высокая цикломатическая сложность действительно указывает на то, что ваши тесты могут стать более сложными. Но иногда просто не существует более простого способа написания кода с высоким уровнем CC и огромными тестами. Вам просто нужно знать, когда это справедливая практика и когда что-то не так с вашим дизайном. С другой стороны, уменьшение CC путем разбиения кода на методы вовсе не облегчает задачу тестирования, поскольку код должен рассматриваться в любом случае. Это просто число, которое будет выглядеть "красивее".
Примите во внимание следующее: вам поручено разработать код, который реагирует на нажатия клавиш и выполняет некоторые задачи в зависимости от того, какая клавиша нажата. Устройство имеет только фиксированное количество кнопок, что означает, что программное обеспечение должно быть исчерпывающим и не должно быть расширяемым (используйте этот шаблон команд).
Вы могли бы написать простой switch
оператор, который будет легко читать, вызывая один метод на нажатие кнопки. Это был бы хороший способ быстро и эффективно решить эту задачу. Но контроль над способом получения нажатия клавиш был бы ужасен. Вы должны разделить код? Зачем? Я имею в виду, что он читабелен, отлично справляется со своей задачей, и независимо от того, что вы делаете, ваши тесты все равно должны учитывать каждое нажатие кнопки. Таким образом, от рефакторинга нет никакой выгоды, кроме как уменьшить это число.
Мой совет - узнать, когда цикломатическая сложность является значимым показателем, а когда нет. Кроме того, попробуйте некоторые советы по рефакторингу Майкла из его поста в блоге. У него есть твердый совет.
Представьте, что вы переходите мост с пределом веса в 10000 фунтов и ведете грузовик с 15000 фунтов груза. Чтобы уложиться в лимит, вы разделяете груз на три прицепа, каждый из которых весит 5000 фунтов, и тянете их за грузовиком. Технически это снижает вес грузовика, но нагрузка на мост остается прежней.
Перенос кода из большого метода в более мелкие, непроверенные частные методы аналогичен. Это делает оригинальный метод , кажется менее сложным, и есть какая - то польза для этого. Но если бы это действительно существенно уменьшило сложность исходного метода, то этот метод стало бы легче тестировать. Это не так.
Любая проверка исходного метода должна по-прежнему проверять всю логику в частных методах, которые он вызывает, так же, как если бы весь код в этих новых методах все еще находился в исходном методе. Если мы не смогли протестировать его раньше (или это было очень сложно), то у нас все равно будет та же проблема.
Что помогает, так это то, что мы извлекаем код в частные методы как ступеньку для их изоляции от исходного метода, возможно, даже от исходного класса. Мы можем переместить эти методы в новые классы, а затем внедрить эти классы в исходный класс. Или, в зависимости от языка, возможно, мы можем внедрить методы.
Сделав это, мы можем
- Протестируйте исходный метод, используя имитацию внедренных зависимостей. В макете нет сложности. Он каждый раз делает то, что ему говорят. Теперь проверка исходного метода не включает весь код этих частных методов. Нам нужно только убедиться, что он правильно взаимодействует с этими зависимостями.
- Протестируйте новые классы и методы. Они также меньше и проще, что упрощает их тестирование.