Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

One way to reduce the number of possible paths is to reduce the number of distinct cases, often via refactoring or a simpler yet more general algorithm; to use an extremely contrived example, it's like the difference between

    int addOne(int x) {
      if(x == 0)
        return 1;
      else if(x == 1)
        return 2;
      ...
    }
and

    int addOne(int x) {
      return x + 1;
    }

I always keep in mind the famous Dijkstra quotes about testing and program complexity:

"Program testing can be used to show the presence of bugs, but never to show their absence!"

"Simplicity is prerequisite for reliability."



This is a perfect example of why line/function coverage is a silly measurement. It doesn't take into account global state and function input. Take the second function above, you could get 100% line coverage in just one run. But the function does exactly the same thing as the one where you only got 50% coverage, already things smell fishy. You can also test the function with millions of different inputs, all of them giving the same 100% coverage, and it will work perfectly fine, until suddenly you try addOne(int.max) and it will fail.

What you want to be really sure is state coverage, or input range coverage assuming your functions are pure. Now testing every function from int.min-int.max might seem unrealistic but what you have to do then is constrain the possible range of input or divide into ranges with special cases that you can somehow group together. Say for example int.min, negative numbers, zero, positive numbers and int.max.

Also, just because you covered a line doesn't mean it's correct, the only thing you've really tested is that the program doesn't crash. For the test to be really useful you also need a correct result of the output, added by a human. You can't just randomize input to increase the coverage.


I came to this thread hoping somebody would say pretty much exactly your first paragraph. The author appears to be selling a code coverage tool so of course he falls into the trap. In real life you have to remember that hitting a line once is not the same as showing it to be correct. People who buy into code coverage tools make this mistake a lot.


Sorry but your exemple makes very little sense in the real world. Because what you wrote is obvious.

Imagine now you have a switch with 50 different conditions that have nothing to do with each other and cant be reduced to a simple arithmetic operation.You'd have to test all the paths if you want a high test coverage rate.

All you can do is abstract decision making through FP or OOP(chain of responsability).


I disagree that FP and OOP are the only abstraction methods. Arithmetic can be an excellent abstraction method.

For me, it's a code smell to have a function called "updateScore4". It's often possible to come up with an algebraic statement that gives the same result as a bunch of logic (code paths).

I think userbinator's point is that better code is easier to test as a result of having fewer code paths. Of course there are some gotchas to be aware of (e.g., overflow), but overall I agree.


On the other hand, you can "hide" the different code paths in a single algebraic statement, but even if your tests always execute that statement, you've lost information that you've actually tested the both "paths" of the algebraic statement.

I guess it's easier to use a boolean logic example (in JavaScript):

return myNormalObject || myDefaultObject();

Your code might always execute the myNormalObject half and return it, and even though your code coverage is 100% of the lines, your tests might miss myDefaultObject() code path.

Perhaps some code coverage tools can take this into consideration, though, but then you're back to the original problem..


That's not arithmetic, though. That's logic represented as a one-liner... Not the sort of change I'm talking about.


Most real world problems need extra logic attached to each case of the conceptually simple arithmetic.

All of coloring is just arithmetic, after all, but is rather complex


> I disagree that FP and OOP are the only abstraction methods. Arithmetic can be an excellent abstraction method.

Arithmetic in programming is typically about functions taking in numbers and returning new numbers, ie. returning a new number instead of mutating one of the numbers in-place. That sounds like the spirit of FP, to me. (Though of course arithmetic on fixed-size numbers falls short of this ideal when it comes to overflow and such, and we often don't handle this possibility.)

I don't doubt that there are other abstractions than FP and OOP. But arithmetic looks like FP, to me.


> I don't doubt that there are other abstractions than FP and OOP. But arithmetic looks like FP, to me.

When aikah said "FP", I took that to mean something like using a higher-level "updateScore" function that accepts a "calculateScore" function as an argument. I certainly may have been mistaken, though.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: