Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

For example, if the code generates a random int from 0-10, and takes a different branch on each outcome, how can one design a test suite to guarantee 100% statement coverage in such code?

In Java, the code might be something like:

int i = new Random().nextInt(10);
switch(i)
{
    //11 case statements
}
share|improve this question
add comment

5 Answers

up vote 19 down vote accepted

Expanding David's answer whom I totally agree with that you should create a wrapper for Random. I wrote pretty much the same answer about it earlier in a similar question so here is a "Cliff's notes version" of it.

What you should do is to first create the wrapper as an interface (or abstract class):

public interface IRandomWrapper {
    int getInt();
}

And the concrete class for this would look like this:

public RandomWrapper implements IRandomWrapper {

    private Random random;

    public RandomWrapper() {
        random = new Random();
    }

    public int getInt() {
        return random.nextInt(10);
    }

}

Say your class is the following:

class MyClass {

    public void doSomething() {
        int i=new Random().nextInt(10)
        switch(i)
        {
            //11 case statements
        }
    }

}

In order to use the IRandomWrapper correctly you need to modify your class to take it as a member (through constructor or a setter):

public class MyClass {

    private IRandomWrapper random = new RandomWrapper(); // default implementation

    public setRandomWrapper(IRandomWrapper random) {
        this.random = random;
    }

    public void doSomething() {
        int i = random.getInt();
        switch(i)
        {
            //11 case statements
        }
    }

}

You can now test your class's behaviour with the wrapper, by mocking the wrapper. You can do this with a mocking framework, but this is easy to do by yourself as well:

public class MockedRandomWrapper implements IRandomWrapper {

   private int theInt;    

   public MockedRandomWrapper(int theInt) {
       this.theInt = theInt;
   }

   public int getInt() { 
       return theInt;
   }

}

Since your class expects something that looks like an IRandomWrapper you can now use the mocked one to force the behaviour in your test. Here are some examples of JUnit tests:

@Test
public void testFirstSwitchStatement() {
    MyClass mc = new MyClass();
    IRandomWrapper random = new MockedRandomWrapper(0);
    mc.setRandomWrapper(random);

    mc.doSomething();

    // verify the behaviour for when random spits out zero
}

@Test
public void testFirstSwitchStatement() {
    MyClass mc = new MyClass();
    IRandomWrapper random = new MockedRandomWrapper(1);
    mc.setRandomWrapper(random);

    mc.doSomething();

    // verify the behaviour for when random spits out one
}

Hope this helps.

share|improve this answer
3  
Totally agree with this. You test a random event by removing the random nature of the event. The same theory can be used for timestamps –  Richard Nov 3 '11 at 12:33
3  
Note: this tehcnique, of giving an object the other object it needs, instead of letting him instanciate them, is called Dependency Injection –  Clement Herreman Nov 3 '11 at 15:19
add comment

You can (should) wrap the random generation code in a class or method and then mock/override it during tests to set the value you want, so that your tests are predictable.

share|improve this answer
add comment

You've got a specified range (0-10), and a specified granularity (whole numbers). So when testing, you don't test with the random numbers. You test within a loop that hits each case in turn. I'd advise passing the random number into a sub-function containing the case statement, which allows you to just test the sub-function.

share|improve this answer
    
much better (because more simple) than what I suggested, wish I could transfer my upvotes :) –  David Nov 3 '11 at 14:08
    
Actually you should do both. Test with a mock RandomObject to test each branch individually, and test repeatedly with real RandomObject. The former is a unit test, the latter more like an integrationt test. –  sleske Nov 3 '11 at 15:22
add comment

Use QuickCheck! I just started playing with this recently and it is amazing. Like most cool ideas it comes from Haskell but the basic idea is that instead of giving your test pre canned test cases you let your random number generator build them for you. That way instead of the 4-6 cases that you would probably come up with in xUnit you can have the computer try hundreds or thousands of inputs and see which ones don't conform to the rules you have set.

Also QuickCheck will when it finds a failing case try to simplify it so that it can find the simplest possible case that does fail. (And of course when you do find a failing case you can then build it into a xUnit test as well)

There appear to be at least two versions for Java so that part should not be a problem.

share|improve this answer
add comment

You can use PowerMock library to mock the Random class and stub its nextInt() method to return the expected value. No need to change your original code if you don't want to.

I'm using PowerMockito and just tested a method similar to yours. For the code that you posted the JUnit test should look something like this:

@RunWith(PowerMockRunner.class)
@PrepareForTest( { Random.class, ClassUsingRandom.class } ) // Don't forget to prepare the Random class! :)

public void ClassUsingRandomTest() {

    ClassUsingRandom cur;
    Random mockedRandom;

    @Before
    public void setUp() throws Exception {

        mockedRandom = PowerMockito.mock(Random.class);

        // Replaces the construction of the Random instance in your code with the mock.
        PowerMockito.whenNew(Random.class).withNoArguments().thenReturn(mockedRandom);

        cur = new ClassUsingRandom();
    }

    @Test
    public void testSwitchAtZero() {

        PowerMockito.doReturn(0).when(mockedRandom).nextInt(10);

        cur.doSomething();

        // Verify behaviour at case 0
     }

    @Test
    public void testSwitchAtOne() {

        PowerMockito.doReturn(1).when(mockedRandom).nextInt(10);

        cur.doSomething();

        // Verify behaviour at case 1
     }

    (...)

You can also stub the nextInt(int) call to receive any parameter, in case you want to add more cases at your switch:

PowerMockito.doReturn(0).when(mockedRandom).nextInt(Mockito.anyInt());

Pretty, isn't it? :)

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.