我有一行代码有问题,比如:
$user->has('roles', ORM::factory('role', array('name' => 'unverified')))
我可以嘲笑第一个参数,但只能断言第二个参数返回一个类。在某些类中,我多次使用has
,并且需要能够正确地测试它们。为了明确起见,我需要确认"未经验证"已被传递到第二个参数的工厂方法中。非常感谢您的帮助。
我正在测试的类:
<?php
/**
* Policy class to determine if a user can upload a file.
*/
class Policy_File_Upload extends Policy
{
const NOT_LOGGED_IN = 1;
const NOT_VERIFIED = 2;
public function execute(Model_ACL_User $user, array $extra = NULL)
{
if ($user->can('login'))
{
return self::NOT_LOGGED_IN;
}
elseif ($user->has('roles', ORM::factory('role', array('name' => 'unverified'))))
{
return self::NOT_VERIFIED;
}
return TRUE;
}
}
以及相应的测试:
public function test_guest()
{
// User mock
$user = $this->getMock('Model_User', array('can', 'has'), array(), '', FALSE);
$user->expects($this->any())->method('can')->with('login')->will($this->returnValue(TRUE));
$user->expects($this->any())->method('has')->with('roles', $this->logicalOr
(
))
->will($this->returnCallback(array($this, 'roles')));
$policy = new Policy_File_Upload;
$this->assertSame(Policy_File_Upload::NOT_VERIFIED, $policy->execute($user));
}
您总是可以走数据驱动的路线,为每个案例准备测试数据,并验证调用函数的结果。最后,您不太关心'unverified'
被传递给ORM::factory()
,而是基于输入从execute()
中得到正确的结果。
测试实现细节会使测试变得脆弱且难以编写。如果你可以测试结果,那么当你改变得出结果的方式时,你的测试就不会中断。为所有用例组合一个数据集可能需要更长的时间,但通常可以在测试之间共享。
为了明确起见,我需要确认"未经验证"已被传递到第二个参数的工厂方法中。
<?php
public function execute(Model_ACL_User $user, array $extra = NULL)
[...] ORM::factory('role', array('name' => 'unverified')) [...]
对不起,伙计,你不能这么做,因为没有(理智的)方法来模拟静态方法调用。
为什么这是一个问题的论点与Sebastian Bergmann的博客文章类似:Testing-Code-That-Uses-Singletons
有像runkit
这样的选项,您可以选择在运行时用模拟版本替换"ORM"类,但这真的很痛苦。
因此,有几个选择:
您可以创建一个类似于Policy_File_Upload_Data_Provider
的东西,它有一个->getRoles
方法,用于放置ORM访问。这将允许您很好地测试业务逻辑,并且测试数据访问类应该更容易一些,因为它不需要那么多(只是访问orm)。
你可以注入你的ORM类(或者如果这不起作用,也许只是它的名字)。根据类本身,您应该能够创建一个实例并调用->factory,就像它是一个普通的方法一样。你可以嘲笑它。
如果你不想这样,把它留在里面也是一个不错的选择。
希望这至少能给你一个想法/概述。
其他链接为什么很难测试静态:
Misko Hevery - Flaw: Brittle Global State & Singletons
Kore Nordmann - Static considered harmful
您实际上可以使用PHP的Phake-mocking框架中的参数捕获来验证传递给mock方法的参数。
public function test_guest()
{
// User mock
$user = Phake::mock('Model_User');
when($user)->can('login')->thenReturn(FALSE);
when($user)->has('roles', $this->anything())->thenGetReturnByLambda(array($this, 'roles'));
$policy = new Policy_File_Upload;
$this->assertSame(Policy_File_Upload::NOT_VERIFIED, $policy->execute($user));
Phake::verify($user)->has('roles', Phake::capture($factoriedRole));
$this->assertEquals('unverified', $factoriedRole->get('name'));
}
本例假设您有能力检查$factoriedRole上的名称,显然这一部分需要根据ORM堆栈的工作方式进行更改。
关于静力学,我同意埃多里安的观点。如果可能的话,我总是倾向于重新设计代码,而不需要静态,但有时这是不可能的,在这种情况下,这是一个可行的解决方案。
Phake Github页面
关于参数捕获的Phake文档