我有一个创建大型多维数组的方法。我正在尝试对此方法运行一系列单元测试。我正在尝试进行积极测试(测试某些数组键是否已设置)和消极测试(测试某些数组键是否不存在)。问题是设置该对象需要大量代码,并且该方法接受我想要测试的许多不同参数。由于这些原因,我想使用dataproviders对该方法运行一系列测试。这样我就可以设置对象一次并使用数据提供程序来获取数组参数和预期的数组值。
$this->assertArraySubset()
并在数据提供程序中包含预期的数组结构来进行积极测试。但我想不出一个好方法来测试某些数组键不存在(我的否定测试),因为这些数组键位于数组的不同级别。
这是我的代码示例,以便您可以看到我正在处理的内容:
<?php
class MyClassTest {
public function providerForFunctionThatCreatesArray() {
return [
[
'{foo:bar}', # some data returned by service A
'{foo:baz}', # some data returned by service B
'c' # checking that this key does not exist in the array
],
[
'{foo:barbaz}',
'{foo:bazbar}',
'd' # I also want to check that this key does not exist but in a different level of the array (i.e. $array['b'])
],
]
}
/**
* @dataProvider providerForFunctionThatCreatesArray
*/
public function testFunctionThatCreatesArray($dataFromServiceA, $dataFromServiceB, $expectedKeyNotExists) {
$serviceA = $this
->getMockBuilder(ServiceA::class)
->setMethods(['get_data'])
->getMock();
$serviceA->expects($this->any())->method('get_data')->willReturnValue($dataFromServiceA);
$serviceB = $this
->getMockBuilder(ServiceB::class)
->setMethods(['get_data'])
->getMock();
$serviceB->expects($this->any())->method('get_data')->willReturnValue($dataFromServiceB);
$myClass = new MyClass($serviceA, $serviceB);
$array = $myClass->functionThatCreatesArray();
// This is the function that checks that keys do not exist in the array
$this->assertArrayNotHasKey($expectedKeyNotExists, $array['a']);
}
}
{foo:...}
是我的函数使用的某些服务返回的数据。不同的值会影响我的函数创建的数组。我已经为这些服务创建了模拟,并使用数据提供者来强制服务返回的值。
如您所见,我的数据提供程序还返回一个键作为我的测试函数的第三个参数(
$expectedKeyNotExists
)。这是我正在检查的键在我的数组中不存在。然而,d
键是我想在数组的不同部分检查的键,例如$array['b']
而不是$array['a']
。如果我运行上面的测试,它将检查 $array['a']
处不存在“d”,这不是我想要的。 构建测试以动态检查我的键是否存在于数组的不同部分中的好方法是什么?
我考虑让我的数据提供者返回第四个密钥,这是要使用的父密钥。像这样:
return [
[
'{foo:bar}', # some data returned by service A
'{foo:baz}', # some data returned by service B
'c', # checking that this key does not exist in the array
'a' # parent key
],
[
'{foo:barbaz}', # some data returned by service A
'{foo:bazbar}', # some data returned by service B
'd', # checking that this key does not exist in the array
'b' # parent key
]
]
然后我可以像这样进行测试:
public function testFunctionThatCreatesArray($dataFromServiceA, $dataFromServiceB, $expectedKeyNotExists, $parentKey) {
// ... snip ...
$this->assertArrayNotHasKey($expectedKeyNotExists, $array[$parentKey]);
}
上述方法的问题在于,在检查数组不同级别的键的情况下,它不是很灵活。例如,如果我想检查
$array['a']['e']['f']
和 $array['a']['g']['h']
处不存在键该怎么办。
据我所知,Phpunit 不递归地提供数组键的断言。
您可以使用自己的断言来扩展 Phpunit,但我会简单地开始并向测试用例添加一个私有辅助方法,该方法返回一个 bool 值,无论数组是否递归地作为键(检查现有的问答材料,例如 Search for a key在数组中,递归地以及其他如何递归地检查数组中的键),然后对 false 进行断言,例如:
$this->assertFalse(
$this->arrayHasKeyRecursive($array, $expected),
"key must not exist"
);
请记住,当您编写代码来支持测试时,要使其变得非常愚蠢(有时您还需要测试辅助例程,以便您的测试不会在错误上欺骗您)。
交叉参考
我知道这是一个老问题,但我解决检查密钥是否不存在的方法是使用 php
array_key_exists
函数。
$this->assertFalse(array_key_exists($key, $array));