Mocking Math.random() using PowerMock

Let’s consider the below Game class in a guessing game where a random target is chosen by the system and the user guesses the target. The system returns an appropriate message based on whether the guess was higher, lower or equal to the random target.

package org.game;

public class Game {
  private int target = (int) (Math.random() * 100);

  public int guess(int guess) {
    return target - guess;
  }
}

No magic there. Now let’s look at the unit test for this class.

package org.game;

// imports hidden;

public class GameTest {
 private Game game;

 @Before
 public void setUp() {
  game = new Game();
 }

 @Test
 public void testHigherGuess() {
  int result = game.guess(55);
  Assert.assertTrue(result < 0);
 }

 @Test
 public void testLowerGuess() {
  int result = game.guess(35);
  Assert.assertTrue(result > 0);
 }

 @Test
 public void testCorrectGuess() {
  int result = game.guess(50);
  Assert.assertTrue(result == 0);
 }
}

As you might have guessed by now this wouldn’t work as the target is randomly generated. Now we have three options to handle this.

  1. Add a constructor taking the target value as argument and set the target using that. This would make passing a non-random value for testing simple. However, this would change the interface of the class unnecessarily.
  2. Access “target” using reflection and set its value after object creation. However, this would make the unit test fragile as it knows too much about the internals of the class under test.
  3. We can make Math.random() to return a constant value within our test case. This is the approach demonstrated in this post.

PowerMock is a mocking framework which extends several popular frameworks to allow mocking of static methods and more. In this post we’ll use PowerMocks’ extension for EasyMock. The general technique to mock static methods using PowerMock is outlined here.

This wouldn’t work for system classes like Math. To workaround this, we can prepare the class using the static method for test rather than the class containing the static method. Which means we’d be using @PrepareForTest(Game.class) instead of @PrepareForTest(Math.class) in our case. The completed unit test would look like this.

package org.game;

// imports hidden

@RunWith(PowerMockRunner.class)
@PrepareForTest(Game.class) // Preparing class under test.
public class GameTest {
 private Game game;

 @Before
 public void setUp() {
  // Mocking Math.random()
  PowerMock.mockStatic(Math.class);
  EasyMock.expect(Math.random()).andReturn(0.50).anyTimes();
  PowerMock.replay(Math.class);

  game = new Game();
 }

 @Test
 public void testHigherGuess() {
  int result = game.guess(55);
  Assert.assertTrue(result < 0);
 }

 @Test
 public void testLowerGuess() {
  int result = game.guess(35);
  Assert.assertTrue(result > 0);
 }

 @Test
 public void testCorrectGuess() {
  int result = game.guess(50);
  Assert.assertTrue(result == 0);
 }
}

Now PowerMock ensures that any call to Math.random() would always return a constant value within the Game class when used for testing. Hence we can test the guess() method just as how any client code using the Game class would.

01 Nov, 2009 java / testing