我已经在我的类中为php time()
方法创建了一个小包装函数
class MyClass
{
public function myFuncion($unixTimeStamp)
{
$pending = $this->isPending($unixTimeStamp)
// data manipulation
return $result
}
protected function isPending($unixTimeStamp)
{
if ($unixTimeStamp > $this->currentTime()) {
return true;
}
return false;
}
public function currentTime()
{
return time();
}
}
我想在这个类中测试公共函数myFunction()
,但我有点困惑如何在不模拟SUT本身(MyClass)的情况下模拟currentTime方法
正确的做法是什么?我觉得用单个方法(getCurrentTime)创建一个时间类并将其注入MyClass
,虽然正确,但过度了,因为我只检查代码中一个地方的时间。
无论如何这是最好的方法吗?
编辑:我正在考虑使time
特性,因为它看起来像PhpUnit可以模拟这一点。仍然不确定这对于在单个方法中使用是否过度…
我还有其他选择吗?
您可以创建测试类的Partial模拟对象,以便仅修改所选方法(currentTime
方法)的行为。为此,您可以使用Mock Builder API的setMethods
:
setMethods(array $methods)可以在Mock Builder对象上调用指定要用可配置测试替换的方法翻倍。其他方法的行为没有改变。如果你打电话setMethods(NULL),则不替换任何方法。
那么试试下面的代码(假设myFunction
返回isPending
方法的结果):
class MyClassTest extends 'PHPUnit_Framework_TestCase
{
/**
* @test
*/
public function itShouldReturnTrueIfPendingState()
{
$currentTime = (new 'DateTime('now -1 year'))->getTimestamp();
/** @var MyClass|'PHPUnit_Framework_MockObject_MockObject $myClass */
$myClass = $this->getMockBuilder(MyClass::class)
->disableOriginalConstructor()
->setMethods(['currentTime'])
->getMock();
$myClass
->method('currentTime')
->willReturn($currentTime);
$this->assertTrue($myClass->myFunction(time()));
}
/**
* @test
*/
public function itShouldReturnFalseIfNotState()
{
$currentTime = (new 'DateTime('now +1 year'))->getTimestamp();
/** @var MyClass|'PHPUnit_Framework_MockObject_MockObject $myClass */
$myClass = $this->getMockBuilder(MyClass::class)
->disableOriginalConstructor()
->setMethods(['currentTime'])
->getMock();
$myClass
->method('currentTime')
->willReturn($currentTime);
$this->assertFalse($myClass->myFunction(time()));
}
我们可以在命名空间级别重写time()
函数。查看https://www.schmengler-se.de/en/2011/03/php-mocking-built-in-functions-like-time-in-unit-tests/
,但缺点是,每次我们调用time()
,它将返回模拟时间:)
namespace MyClass;
function time()
{
return MyClassTest::$mockTime ?: 'time();
}
class MyClassTest extends TestCase{
public static $mockTime;
public function test_my_function(){
self::$mockTime = '08:10';
$myClass = new MyClass();
//$myClass->currentTime() //will return 08:10
//do something with my function
}
我知道我有点晚了,但是如果您能够在您的环境中安装php扩展,您可以使用https://github.com/slope-it/clock-mock,它可以透明地工作,无需修改您的代码。此时,您可以删除currentTime()
,只使用time()
。