如何用 phpunit 模拟非抽象特征方法?



我有一个特征(由另一个特征使用)和一个非抽象方法,我需要模拟,但是当我尝试这样做时,我收到错误消息:

尝试配置方法getOfficeDays,该方法由于不存在、尚未指定、最终方法或静态而无法配置

这是我正在尝试测试的代码:

trait OfficeDaysTrait {
public function getOfficeDays(): array {
// This normally calls the database, that's why I want to mock it in tests
return [
[
'day' => 'mon',
'openingTime' => '08:00:00',
'closingTime' => '17:00:00'
]
];
}
}
trait EmployeeAttendenceTrait {
use OfficeDaysTrait;
public function isLate($today)
{
$late = false;
$officeDays = $this->getOfficeDays();
// compare logic with today.. whether the employee is late or not
return $late;
}
}

这是我的测试方法主体:

$mock = $this->getMockForTrait(EmployeeAttendenceTrait::class); 
$mock->expects($this->once())
->method('getOfficeDays')
->will([])
;
$this->assertTrue($mock->isLate());

此语法基于 phpunit 文档中用于测试特征的示例

为什么我会收到此错误消息,如何模拟getOfficeDays以测试我的isLate方法?

没有被嘲笑的方法不能有期望

我将在下面详细介绍,但这个:

$mock = $this->getMockForTrait(EmployeeAttendenceTrait::class); 

是嘲笑没有方法,因此$mock相当于:

class Testing extends PHPUnitsInternalMockStub
{
use EmployeeAttendenceTrait;
}
$mock = new Testing;

检查错误消息

错误消息如下:

尝试配置方法 getOfficeDays 无法配置,因为它不存在、尚未指定、是最终的或静态的

让我们来看看每种可能性(几乎依次)

它不存在

代码看起来显然有一个名为getOfficeDays的方法 - 如果有疑问,使用 get_class_methods 会澄清:

<?php
use PHPUnitFrameworkTestCase;
class SOTest extends TestCase
{
public function testMethods()
{
$mock = $this->getMockForTrait(EmployeeAttendenceTrait::class);
print_r(get_class_methods($mock));
}
}

哪些输出:

R                                                                   1 / 1 (100%)Array
(
[0] => __clone
[1] => expects
[2] => method
[3] => __phpunit_setOriginalObject
[4] => __phpunit_getInvocationMocker
[5] => __phpunit_hasMatchers
[6] => __phpunit_verify
[7] => isLate
[8] => getOfficeDays
)

Time: 569 ms, Memory: 12.00MB
There was 1 risky test:
1) SOTest::testMethods
This test did not perform any assertions

所以不是那样。

是最终的

public function getOfficeDays(): array {

方法签名显然不会将方法声明为 final,所以事实并非如此。

或静态

public function getOfficeDays(): array {

方法签名显然也不是静态的 - 所以也不是这样。

尚未指定

通过逻辑推论,这必须是问题所在 - 它是唯一需要对使用phpunit有所了解的问题。

getMockForTrait的文档阅读(强调):

getMockForTrait() 方法返回一个使用指定特征的模拟对象。给定特征的所有抽象方法都被嘲笑。这允许测试性状的具体方法。

由于没有一个特征方法都是抽象的,因此不会用问题中的语法来模拟任何方法。更深入地看,getMockForTrait 的方法签名有更多的参数:

/**
* Returns a mock object for the specified trait with all abstract methods
* of the trait mocked. Concrete methods to mock can be specified with the
* `$mockedMethods` parameter.
*
* @throws RuntimeException
*/
public function getMockForTrait(
string $traitName, 
array $arguments = [], 
string $mockClassName = '', 
bool $callOriginalConstructor = true, 
bool $callOriginalClone = true, 
bool $callAutoload = true, 
array $mockedMethods = [], 
bool $cloneArguments = true): MockObject

有一个参数用于指定显式模拟的方法,因此编写测试如下:

class SOTest extends TestCase
{
public function testMethods()
{
$mock = $this->getMockForTrait(
EmployeeAttendenceTrait::class, 
[],   # These
'',   # are
true, # the
true, # defaults
true, #
['getOfficeDays']
);
$mock->expects($this->once())
->method('getOfficeDays')
->willReturn([]);
$today = new DateTime(); // or whatever is appropriate
$this->assertTrue($mock->isLate($today));
}
}

将生成有效的模拟对象并允许测试运行。

最新更新