我正在使用 PHPunit 测试我的代码。我的代码有几种排序方法:按姓名、年龄、计数和随机。下面是按计数排序的实现和测试。这些都是很琐碎的事情。
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;
}
}
测试:
/**
* 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);
}
但是现在,我想添加“随机排序”
/**
* Sort random.
*/
public function testSortRandom() {
//what to test? That "shuffle" got called? That the resulting array
// has "any" ordering?
}
实现可以是任何形式,从调用
shuffle($this->tags)
到随机返回 0、-1 或 +1 的 usort
回调。性能是一个问题,但可测试性更重要。
如何测试数组是否随机排序? AFAIK 很难存根像
shuffle
这样的全局方法。
假设您正在使用 shuffle 您的方法应该如下所示
sortRandom() {
return shuffle($this->tags);
}
好吧,你不需要测试键是否被打乱,但是否仍然返回数组。
function testSortRandom(){
$this->assertTrue(is_array($this->object->sortRandom()));
}
您应该测试您的代码,而不是 php 核心代码。
从任何有意义的意义上来说,这实际上都是不可能的。如果您有一个只有几个项目的列表,那么完全有可能按随机排序确实看起来像按任何给定字段排序(并且碰巧它的顺序与按排序顺序相同)如果你没有太多元素,任何其他字段都相当高)
如果你问我排序操作是否实际上没有以任何方式操作数据,对排序操作进行单元测试似乎有点愚蠢。感觉像是为了单元测试而进行,而不是因为它实际上是在测量某些东西是否按预期工作。
我决定用全局包装器来实现它:
class GlobalWrapper {
public function shuffle(&$array);
shuffle($array);
}
}
在排序中,我通过该包装器调用
shuffle
:
public function sort($by_property) {
if ($by_property == "random") {
$this->global_wrapper()->shuffle($this->tags);
}
//...
}
然后,在测试中我可以模拟
GlobalWrapper
并为感兴趣的全局函数提供存根。在这种情况下,我感兴趣的是该方法被调用,而不是它输出的内容[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] 实际上,我也对这个包装器进行了单元测试,测试参数以及它执行按引用调用的事实。此外,这个包装器已经实现(并称为
DrupalWrapper
),以允许我存根第三方(Drupal)提供的某些全局函数。此实现允许我使用 set_drupal()
传递包装器并使用 drupal()
获取它。在上面的例子中,我将它们称为 set_global_wrapper()
和 global_wrapper()
。
在某些情况下,我们应该确保结果被打乱。
例如
function sortRandom(): Collection {
return shuffle($this->tags);
}
function testSortRandom() {
$responseA = $this->object->sortRandom()->toArray();
$responseB = $this->object->sortRandom()->toArray();
// We only check the first few results.
// The order of at least one of them should be different.
$this->assertTrue(
$responseA[0]['name'] != $responseB[0]['name'] ||
$responseA[1]['name'] != $responseB[1]['name'] ||
$responseA[2]['name'] != $responseB[2]['name']
);
}