如何用 phpunit 模拟非抽象特征方法?
How to mock a non-abstract trait method with phpunit?
我有一个特征(被另一个特征使用)和一个我需要模拟的非抽象方法,但是当我尝试这样做时我收到错误消息:
Trying to configure method getOfficeDays
which cannot be configured because it does not exist, has not been specified, is final, or is static
这是我要测试的代码:
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's documentation for testing traits
中显示的示例
为什么我会收到此错误消息以及如何模拟 getOfficeDays
才能测试我的 isLate
方法?
没有被mock过的方法不能有期望
我将在下面详细介绍,但是:
$mock = $this->getMockForTrait(EmployeeAttendenceTrait::class);
没有模拟任何方法,因此 $mock
等同于:
class Testing extends PHPUnitsInternalMockStub
{
use EmployeeAttendenceTrait;
}
$mock = new Testing;
检查错误信息
错误信息如下:
Trying to configure method getOfficeDays which cannot be configured because it does not exist, has not been specified, is final, or is static
让我们看看每一种可能性(几乎依次)
不存在
代码 看起来 显然有一个名为 getOfficeDays
的方法 - 如果有疑问,使用 get_class_methods 会澄清:
<?php
use PHPUnit\Framework\TestCase;
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 {
方法签名显然没有 declare the method as final - 所以不是那样。
或者是静态的
public function getOfficeDays(): array {
方法签名很清楚is also not static - 所以也不是那样。
尚未指定
根据逻辑推论,这个 是问题所在 - 而且它是唯一一个需要对使用 phpunit 有所了解的问题。
docs for getMockForTrait阅读(强调):
The getMockForTrait() method returns a mock object that uses a specified trait. All abstract methods of the given trait are mocked. This allows for testing the concrete methods of a trait.
由于 none 特征方法是抽象的,因此不应使用问题中的语法模拟任何方法。更深入地看,method signature for 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));
}
}
将生成一个有效的模拟对象并允许测试 运行。
我有一个特征(被另一个特征使用)和一个我需要模拟的非抽象方法,但是当我尝试这样做时我收到错误消息:
Trying to configure method
getOfficeDays
which cannot be configured because it does not exist, has not been specified, is final, or is static
这是我要测试的代码:
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's documentation for testing traits
中显示的示例为什么我会收到此错误消息以及如何模拟 getOfficeDays
才能测试我的 isLate
方法?
没有被mock过的方法不能有期望
我将在下面详细介绍,但是:
$mock = $this->getMockForTrait(EmployeeAttendenceTrait::class);
没有模拟任何方法,因此 $mock
等同于:
class Testing extends PHPUnitsInternalMockStub
{
use EmployeeAttendenceTrait;
}
$mock = new Testing;
检查错误信息
错误信息如下:
Trying to configure method getOfficeDays which cannot be configured because it does not exist, has not been specified, is final, or is static
让我们看看每一种可能性(几乎依次)
不存在
代码 看起来 显然有一个名为 getOfficeDays
的方法 - 如果有疑问,使用 get_class_methods 会澄清:
<?php
use PHPUnit\Framework\TestCase;
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 {
方法签名显然没有 declare the method as final - 所以不是那样。
或者是静态的
public function getOfficeDays(): array {
方法签名很清楚is also not static - 所以也不是那样。
尚未指定
根据逻辑推论,这个 是问题所在 - 而且它是唯一一个需要对使用 phpunit 有所了解的问题。
docs for getMockForTrait阅读(强调):
The getMockForTrait() method returns a mock object that uses a specified trait. All abstract methods of the given trait are mocked. This allows for testing the concrete methods of a trait.
由于 none 特征方法是抽象的,因此不应使用问题中的语法模拟任何方法。更深入地看,method signature for 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));
}
}
将生成一个有效的模拟对象并允许测试 运行。