JavaDeveloper
JavaDeveloper

Reputation: 5670

What combinations of unit tests should be applied for code branches?

If I am not too sure of which of following tests would be necessary or redundant. Consider unit testing the following code:

public class Locker {

    public enum Type { FOO, BAR, FOOBAR };

    private Locker() {}

    public boolean shouldlock (int x) {
        return x > 10;
    }

    public boolean lock (Type type, int x) {
        switch (type) {
            case FOO : return shouldlock(x);
            case BAR : return shouldlock(x * 2);
        }
        return false;
    }
}

Test case 1: test shouldlock for both true and false case. - no doubt so far.

Question 1:

Test case 2: lock for input type Foo also be tested for true and false cases, ie both of them. ? Underlying its calling shouldlock and we have already tested both cases for it. Thus it may be redundant. But not too sure.

Question 2:

Test case 3: Assuming answer to Question1 is true, do we still need to test Bar for true and false case ?

Question 3:

Test case 4: Assuming answer to both Question1 and Question2 is true. Assume now shouldlock is changed to private (just assume it). Would the only difference in testing be that I should omit Test case 1 ?

Question 4:

Test case 5: Is it required to check that the only left out enum FOOBAR returns false ?

Question 5:

Assuming answer to question 4 is true, then what if enum tomorrow contains 100 more items ? How to scale such a test ?

Upvotes: 2

Views: 301

Answers (3)

Vidya
Vidya

Reputation: 30320

I know this is a contrived example for the sake of the question, so I will answer in general.

You want to test with as many inputs as it takes to satisfy yourself that the code works. Sure, you might miss something, but the test will be there when you need to recreate the bug later.

In the case of shouldlock, I would test with a set of integers that ease my mind that the code works--like a very low negative number, a very high positive number, 0, and more representative numbers you expect as input.

As far as lock, your questions suggest you are thinking in terms of implementation, which I believe is a mistake. You need to think in terms of the clients of the method. Specifically, don't say "Do we still need to test for BAR?" Rather, you just need to think of a reasonable number of ways your method can be called by a client and test accordingly. This means with all your enum values.

In terms of scale, I think the question is moot because the difficulty of testing with a 100-item enum (as unlikely as that is) suggests you should change the implementation, so your method would probably change. Your tests would then reflect that.

As for changing a method to private, then you don't have to test it anymore. Only your public API needs to be tested. You might want to add assertions to those methods though.

The bottom line is don't agonize over 100% test coverage. There are diminishing returns, and when a bug is found, you will have a test available to reproduce it and fix it.

Upvotes: 1

Rangi Keen
Rangi Keen

Reputation: 945

  1. Test Case 1 - Yes, test the true and false cases on the boundary conditions (10, 11).
  2. Test Case 2 - Mock shouldlock and verify that it is called with x (probably using a constant value for the test). Using something like Mockito and have it call the real method only when calling lock.
  3. Test Case 3 - Same as test case 2, but verify shouldlock is called wtih x * 2.
  4. Test case 4 - You'd have to test the boundary cases for each type: FOO with 10 and 11, BAR with 5 and 6.
  5. Test Case 5 - Yes, only one test for FOOBAR is required.
  6. You'll have to write tests for the new values. If there are more enum values but they all go to the default case, no more unit tests are required. If there is additional logic, unit tests should be written (preferably before the production code) to test that logic.

Upvotes: 1

Andrei Nicusan
Andrei Nicusan

Reputation: 4623

First of all, it depends which are the scenarios your code has to take care of. With this perspective, you can think about approaching your work in a test-driven development (TDD) manner, in which you first write the tests that should cover your desired functionality, and than write the code that make your tests pass.

With this approach, there are no general valid answers for your questions. It only depends on what you need and how rigorous you need to be.

Second, you can approach your unit tests with the following idea in mind: make the unit tests code coverage as high as possible. I'm thinking here mostly about the line coverage and the conditional coverage. This means that the code that is executed during unit testing should be comprised of as many lines as possible and it should cover as many conditional branches as possible, respectively.

Also, with this approach, you might want to reduce redundancy as much as possible, that is keep the count of times a line is executed as low as possible, but above 0 (ideally 1). In this case, the answers to your questions are:

  1. Only for one case, either true or false.
  2. Test the BAR case, for one of the cases, but TEST IT (you want to cover the case BAR line.
  3. As long as you cover both true and false cases, yes.
  4. Yes, you want to cover also the return false line.
  5. Code coverage is important, but don't exaggerate when it doesn't pay off. :)

Upvotes: 1

Related Questions