views:

93

answers:

5

I'm using PHPUnit and my traditional approach is to use mock objects and methods that get called by the methods I'm testing. The mock objects are told what to expect as input by the unit test. The problem is that part of the input supplied to the mock objects are being randomly generated by the methods being tested (and the unit test has no way of knowing what those are).

Just wondering if anyone could suggest a solution.

+2  A: 

It's hard to tell without knowing your specific case, but maybe you could refactor to inject some random number provider into your object under test. During your test you could use a hardcoded random number seed to get reproducible results.

Chris Farmer
That's a good suggestion...though I'm not sure if I like the increase in complexity for the sake of a unit test.
rr
A: 

You don't have to use mocks that are strict about their inputs - only do that when it adds value to the tests.

In other scenarios, use stubs, which don't care about their input, or have some methods be strict and some loose as necessary.

orip
+2  A: 

If you have a map of expected results, and you draw randomly from the key set, you can have a degree of randomness and still be able to predict what the expected result is.

duffymo
+1 for avoiding true randomness. If a failed unit test can't always be reproduced, it reduces trust in the value of that unit test
Nader Shirazie
+2  A: 

Can the "random part" be injected into the method (or is the randomness the core feature)?

E.g. (maybe a oversimplified example taking "random" literally) instead of

function foo($x, $y) {
  return $x * rand(1, $y);
}

use something like

function foo($x, $r) {
  return $x * $r->getNext();
}

This way you eliminate as much "randomness" as possible in your tests, since you can pass an object for $r that doesn't really return random values but e.g. edge cases.

VolkerK
You could also use a setter in the class to set the random generator object. This way, it doesn't need to be a parameter to the function. Something along the lines of: function foo($x) { $r = $this->getGenerator(); [...]} function getGenerator() {if(empty($this->generator) {$this->generator = new RandomGenerator();} return $this->generator;}
PatrikAkerstrand
Yes of course, the method of injection is a matter of choice.
VolkerK
I think I'm going with this along with a few similar suggestions above, with the addition of a set method on the object requiring the random generation (so the unit test can set the generator itself and in effect, control the variability). Thanks all.
rr
+1  A: 

I always think of a unit test as something repeatable. (I mean that each run behaves the same and gives the same result).
There are other preconditions for a unit test, but for me, it is the most important one. (Have a look at this very good definition)

Given this, it could looks impossible to test you specific case.
I would try to separate the random thing of your code into a specific part. Then, I would think about it as a data source, and therefore I would try to mock it as well.

I hope you can apply this to your context.

remio