Cyclomatic complexity rightfully reduced by using private methods?
Using private methods for decreasing CC by refactoring some decision points into separate methods decreases the CC of the actual method and eases reading, but does not decrease the effort to get full branch coverage in testing.
Is this ju开发者_高级运维stifyable? What is you field experience?
Well if you're still interested there is this article I wrote down on Cyclomatic Complexity
Cutting your code in different methods won't reduce the complexity but will only help locally to have a better organisation. The only real way to reduce CC is to refactor indeed !
You will need the same amount of tests if you only extract methods. Have a look at you're data structures may be they're not helping you ? Consider cutting in several classes too if it has sens.
Sometimes, making your application code less complex and more readable has as a consequence that your test code becomes more complex and less readable. However, that is not a reason not to do the refactoring. Readability of the production code is more important than your tests.
If you make some methods private for decreasing CC and improving readability, you could use a framework like Mockito to still be able to test the private methods themselves.
In my experience, this is a very common situation - making you production code do things right makes it harder to test
Other examples include hiding implementation details behind interfaces, never reaching ORM entities to outward callers, making certain functionality available over web service calls only...and generally to have an API you would expect in production restricts the testings quite often.
And yes, its a pain to get your coverage back after a substantial refactoring. Sometimes this results in reversing the overall progress when the features that are so hard to test are not tested properly. So I generally agree to Fortega, except the very last sentence. Don't let your tests rot. They'll come back when you want it least.
I don't really understand your problem. Obviously refactoring out logic and decision points out of a large method into private methods will decrease cyclomatic complexity of the large methods. This is kind of the point of extracting methods - it makes the large method shorter and simpler, thus hopefully easier to understand and to change.
It's not cheating, it's simply making the structure of your program explicit.
but does not decrease the effort to get full branch coverage in testing.
This seems a non-sequitur to me. Why should factoring out private methods make coverage testing easier? I have never seen anyone claim that. Did you maybe misunderstand something?
You should not ever refactor your code because of the perception that improving the numbers a metric is spewing out is good for your code. Rather than go for numbers and fancy reports (I have seen this done unfortunately) you should aim at understanding the metric and why it is used in the first place.
Cyclomatic Complexity is a simple mathematical measure that only states how many different paths your code can execute in if all of its branches are navigated. So yes, high Cyclomatic Complexity does indeed indicate that your tests may become more complex. But sometimes there is simply no easier way to write code with high CC and huge tests. You just need to know when this is a fair practice and when something is wrong with your design. On a sidenote, decreasing CC by splitting of code into methods does not at all ease the task of testing as the code has to be covered either way. It is just the number that will look "prettier".
Consider the following: you are tasked to design code that reacts to key presses and perform some task depending on which key is pressed. The device only features a fixed amount of buttons which means that the software needs to be exhaustive in the first place and need not be extendible (take that Command pattern).
You could write a simple switch
statement that would be easy to read, triggering one method per button pressed. This would be a good way to solve this task quickly and efficiently. But the CC of the method getting the key press would be horrible. Should you split of code? Why? I mean it is readable, serves its purpose perfectly and no matter what you do your tests still need to account for each button press. So there is no benefit from refactoring other than decreasing that number.
My advice is to learn when Cyclomatic Complexity is a meaningful metric and when it is not. Also, try some of the refactoring advice of Micheal from his blog post. It has some solid advice.
Imagine that you're crossing a bridge with a weight limit of 10,000 pounds, and you're driving a truck with 15,000 pounds of cargo. To fit within the limit you split the cargo into three trailers, each weighing 5,000 pounds, and you pull them behind your truck. It decreases the weight that the truck is carrying - technically - but the strain on the bridge remains the same.
Moving code from a large method into smaller, untested private methods is similar. It makes the original method seem less complex, and there is some benefit to that. But if it really reduced the complexity of the original method in a meaningful way then that method would become easier to test. It doesn't.
Any test of the original method must still test all of the logic within the private methods it calls, the same as if all the code in those new methods were still in the original method. If we couldn't test it before (or it was very difficult) then we'll still have exactly the same problem.
What helps is if we extract code into private methods as stepping stone to isolating them from the original method, probably even from the original class. We can move those methods into new classes and then inject those classes in to the original class. Or depending on the language perhaps we can inject methods.
Having done that we can
- Test the original method using mocks for the injected dependencies. A mock has no complexity. It does what it's told every time. Now a test of the original method doesn't include all the code in those private methods. We only need to verify that it interacts as expected with those dependencies.
- Test the new classes and methods. They are also smaller and simpler, which makes them easier to test.
精彩评论