views:

106

answers:

6

I am writing unit test cases for a game I am working on. When the game starts, the player is positioned randomly, and I have two problems with that:

  1. Since the player is positioned randomly, I cannot be sure that a test case that passes once will pass again. For example, it could pass most of the time, but fail if the player happens to be positioned in front of an obstacle.
  2. I have to test all situations in one test case. For example, when testing whether the player moves correctly, I have to check whether there was an obstacle and if it was considered by the algorithm.

I'm not really happy with that, but I don't see a way out. Is it acceptable to test methods with partially random behaviour?

+11  A: 

I suggest you treat your source of randomness (a random number generator or whatever) as a dependency. Then you can test it with known inputs by providing either a fake RNG or one with a known seed. That removes the randomness from the test, while keeping it in the real code.

If you fake the RNG, you can test what happens if it would naturally position the player on an obstacle - how it moves the player out of the way, etc. Of course that relies on knowing how the class uses the RNG, but personally I'm happy enough with unit tests acting as "white box tests" with some internal knowledge.

Jon Skeet
So that means you suggest I supply my game with a random number generator of which I can create a mock implementation?
forceal
@forceal: Yup, that's right.
Jon Skeet
Hm. But the random number generator would be used for several things, so it would be really difficult to fake it. For instance, it is also used for the player color (something that has no influence on any other aspect of the game, so I ignore it). The only way I see would be to create a generator class containing methods like "getRandomPlayerPosition()" and "getRandomPlayerColor()", then I could fake it. Would that be OK?
forceal
@forceal: Yes, that would be fine, and easier to control too. Then your "real" generator class could probably be so simple that it didn't need many tests.
Jon Skeet
Okay. Thank you, I like that solution! :)
forceal
@forceal - the "trick" is to realize you aren't testing randomness at this point, instead you are testing your code's response to a number between 0 and 1.
Rodney Gitzel
It worked great! I created an interface "RNG" which has methods like "getRandomPlayerPosition()" and "getRandomPlayerColor()". My game is now created with an instance of RNG. For testing, it uses the "MockRNG" implementation which has setter methods for position and color. In production, it uses "JavaRNG" which uses Java's random number mechanism to return the values.
forceal
+3  A: 

One approach you can take is to split out the random-position generation into a separate class/interface, so that you can override it in your test, and therefore control it.

Grant Crofton
+3  A: 

Make the source of randomness an input to the test, and configure it to be the same each time.

Almost all random number generators take a 'seed' value. By providing the same seed value each time, you can know that you will get the same sequence of random numbers and hence the output of your application should be exactly the same.

I have to test all situations in one test case.

Why? You can have multiple test cases. You can pick any number of different and arbitrary random number seeds to create the conditions you want to test. Or simply replace the random positioning with a specific positioning for the purposes of the test.

Kylotan
A: 

There are two different things to test here, and you should test them separately:

  • Does the program logic work for all possible starting positions? Test this by positioning the player non-randomy at a number of positions, trying to cover all "special cases".
  • Is the random positioning actually random? Test this by calling only the positioning logic many times, and do some sort of statistical analysis.
Michael Borgwardt
A: 

According to testing theory it is not possible to test random behavior :) On the other hand, as in previous answers said, for your tests you could make pseudo random activities somehow.

Andriy Sholokh
A: 

As other answers said, random algorithms are deterministic. you can reproduce a sequence if you use the same seed.

The only situations where I had to deal with a non-deterministic code, is when multithreading was involved. Then, you are dependant on the scheduler of the operating system.

In those cases, I write the unit-test as a loop, that repeats thousand of times the test code.

barjak