I am testing my code with PHPunit. My code has several ordering-methods: by name, age, count and random. Below the implementation and test for sorting by count. These are pretty trivial.
class Cloud {
//...
public function sort($by_property) {
usort($this->tags, array($this, "cb_sort_by_{$by_property}"));
return $this;
}
private function cb_sort_by_name($a, $b) {
$al = strtolower($a->get_name());
$bl = strtolower($b->get_name());
if ($al == $bl) {
return 0;
}
return ($al > $bl) ? +1 : -1;
}
/**
* Sort Callback. High to low
*/
private function cb_sort_by_count($a, $b) {
$ac = $a->get_count();
$bc = $b->get_count();
if ($ac == $bc) {
return 0;
}
return ($ac < $bc) ? +1 : -1;
}
}
Tested with:
/**
* Sort by count. Highest count first.
*/
public function testSortByCount() {
//Jane->count: 200, Blackbeard->count: 100
//jane and blackbeard are mocked "Tags".
$this->tags = array($this->jane, $this->blackbeard);
$expected_order = array("jane", "blackbeard");
$given_order = array();
$this->object->sort("count");
foreach($this->object->get_tags() as $tag) {
$given_order[] = $tag->get_name();
}
$this->assertSame($given_order, $expected_order);
}
But now, I want to add "random ordering"
/**
* Sort random.
*/
public function testSortRandom() {
//what to test? That "shuffle" got called? That the resulting array
// has "any" ordering?
}
The implementation could be anything from calling shuffle($this->tags)
to a usort
callback that returns 0,-1 or +1 randomly. Performance is an issue, but testability is more important.
How to test that the array got ordered randomly? AFAIK it is very hard to stub global methods like shuffle
.
I decided to implement this with a global-wrapper:
class GlobalWrapper {
public function shuffle(&$array);
shuffle($array);
}
}
In the sort, I call shuffle
through that wrapper:
public function sort($by_property) {
if ($by_property == "random") {
$this->global_wrapper()->shuffle($this->tags);
}
//...
}
Then, in the tests I can mock that GlobalWrapper
and provide stubs for global functions that are of interest. In this case, all I am interested in, is that the method gets called, not what it outputs[1].
public function testSortRandomUsesShuffle() {
$global = $this->getMock("GlobalWrapper", array("shuffle"));
$drupal->expects($this->once())
->method("shuffle");
$this->object->set_global_wrapper($drupal);
$this->object->sort("random");
}
[1] In reality I have Unit Tests for this wrapper too, testing the parameters and the fact it does a call-by-ref. Also, this wrapper was already implemented (and called DrupalWrapper
) to allow me to stub certain global functions provided by a third party (Drupal). This implementation allows me to pass in the wrapper using a set_drupal()
and fetch it using drupal()
. In above examples, I have called these set_global_wrapper()
and global_wrapper()
.