模拟包装器时间函数
Mocking a wrapper time function
我在 class
中为 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();
}
}
我想在此 class 中测试 public 函数 myFunction()
但我有点不知所措如何在不模拟 SUT 本身的情况下模拟 currentTime 方法(我的班级)
正确的做法是什么?我觉得使用单一方法 (getCurrentTime) 创建时间 class 并将其正确注入 MyClass
是多余的,因为我只检查代码中一个地方的时间。
这是最好的方法吗?
编辑: 我正在考虑制作一个 time
特性,因为它 looks like PhpUnit 可以模拟它。仍然不确定这对于在单一方法中使用是否过大..
我还有哪些其他选择?
我们可以在命名空间级别覆盖 time()
函数。检查 https://www.schmengler-se.de/en/2011/03/php-mocking-built-in-functions-like-time-in-unit-tests/
但缺点是每次我们调用 time()
它都会 return 模拟时间 :)
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
}
您可以创建已测试 class 的部分模拟对象,以便仅修改所选方法(currentTime
方法)的行为。为此,您可以使用 Mock Builder API:
的 setMethods
setMethods(array $methods) can be called on the Mock Builder object to
specify the methods that are to be replaced with a configurable test
double. The behavior of the other methods is not changed. If you call
setMethods(NULL), then no methods will be replaced.
所以试试这个代码(假设 myFunction
return 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()));
}
我知道我来晚了一点,但如果您能够在您的环境中安装 php 扩展,您可以使用 https://github.com/slope-it/clock-mock,它透明地工作,对您的修改为 0代码。那时,您可以删除 currentTime()
并只使用 time()
.
我在 class
中为 phptime()
方法创建了一个小包装函数
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();
}
}
我想在此 class 中测试 public 函数 myFunction()
但我有点不知所措如何在不模拟 SUT 本身的情况下模拟 currentTime 方法(我的班级)
正确的做法是什么?我觉得使用单一方法 (getCurrentTime) 创建时间 class 并将其正确注入 MyClass
是多余的,因为我只检查代码中一个地方的时间。
这是最好的方法吗?
编辑: 我正在考虑制作一个 time
特性,因为它 looks like PhpUnit 可以模拟它。仍然不确定这对于在单一方法中使用是否过大..
我还有哪些其他选择?
我们可以在命名空间级别覆盖 time()
函数。检查 https://www.schmengler-se.de/en/2011/03/php-mocking-built-in-functions-like-time-in-unit-tests/
但缺点是每次我们调用 time()
它都会 return 模拟时间 :)
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
}
您可以创建已测试 class 的部分模拟对象,以便仅修改所选方法(currentTime
方法)的行为。为此,您可以使用 Mock Builder API:
setMethods
setMethods(array $methods) can be called on the Mock Builder object to specify the methods that are to be replaced with a configurable test double. The behavior of the other methods is not changed. If you call setMethods(NULL), then no methods will be replaced.
所以试试这个代码(假设 myFunction
return 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()));
}
我知道我来晚了一点,但如果您能够在您的环境中安装 php 扩展,您可以使用 https://github.com/slope-it/clock-mock,它透明地工作,对您的修改为 0代码。那时,您可以删除 currentTime()
并只使用 time()
.