我是单位测试的新手,并尝试对使用表单的旧zend应用程序的模型验证进行单元测试。
在其中一个表单中创建了第二个类的实例,我正在努力理解如何模拟依赖对象。表格如下:
class Default_Form_Timesheet extends G10_Form {
public function init() {
parent::init();
$this->addElement( 'hidden', 'idTimesheet', array( 'filters' => array ('StringTrim' ), 'required' => false, 'label' => false ) );
$this->addElement('checkbox', 'storyFilter', array('label' => 'Show my stories'));
$user = new Default_Model_User();
$this->addElement('select', 'idUser', array('filters' => array('StringTrim'), 'class' => 'idUser', 'required' => true, 'label' => 'User'));
$this->idUser->addMultiOption("","");
$this->idUser->addMultiOptions($user->fetchDeveloper());
...
......
调用$ user-> fetchDeveloper()时出现问题。我怀疑它有嘲弄对象和依赖注入的东西,但任何指导将不胜感激。我的失败单元测试内容如下......
require_once TEST_PATH . '/ControllerTestCase.php';
class TimesheetValidationTest extends ControllerTestCase {
public $Timesheet;
public $UserStub;
protected function setUp()
{
$this->Timesheet = new Default_Model_Timesheet();
parent::setUp();
}
/**
* @dataProvider timesheetProvider
*/
public function testTimesheetValid( $timesheet ) {
$UserStub = $this->getMock('Default_Model_User', array('fetchDeveloper'));
$UserStub->expects( $this->any() )
->method('fetchDeveloper')
->will( $this->returnValue(array(1 => 'Mickey Mouse')));
$Timesheet = new Default_Model_Timesheet();
$this->assertEquals(true, $Timesheet->isValid( $timesheet ) );
}
我的数据提供者位于单独的文件中。
它在命令行终止,没有输出,我有点难过。任何帮助将不胜感激。
答案 0 :(得分:1)
您无法在测试中模拟表单中的Default_Model_User
类。因为您的代码在内部实例化了类,所以无法用模拟替换它。
您可以使用以下几种方法来测试此代码。
你研究fetchDeveloper
正在做什么并控制它返回的内容。通过模拟对象,你可以注入某个地方(看起来不太可能)或者通过设置一些数据,以便你知道数据是什么。这将使您的测试变得有点脆弱,因为当您使用的数据发生变化时,它可能会中断。
另一个选项是重构代码,以便您可以将模拟传递到表单中。您可以设置一个构造函数,允许您设置Default_Model_User
类,然后您可以使用您编写的测试来模拟它。
构造函数是这样的:
class Default_Form_Timesheet extends G10_Form {
protected $user;
public function __construct($options = null, Default_Model_User $user = null){
if(is_null($user)) {
$user = new Default_Model_User();
}
$this->user = $user;
parent::__construct($options);
}
Zend Framework允许将选项传递给表单构造函数,我不确定您是否在任何地方使用代码,这样就不会破坏您当前的任何功能。何时可以再次传递可选的Default_Model_User
,以免破坏您当前的功能。您需要在调用$this->user
之前设置parent::__construct
的值,否则Zend将抛出错误。
现在你的init函数必须改为:
$user = new Default_Model_User();
到
$user = $this->user;
在测试中,您现在可以传入模拟对象并将其使用。
public function testTimesheetValid( $timesheet ) {
$UserStub = $this->getMock('Default_Model_User', array('fetchDeveloper'));
$UserStub->expects( $this->any() )
->method('fetchDeveloper')
->will( $this->returnValue(array(1 => 'Mickey Mouse')));
$Timesheet = new Default_Model_Timesheet(null, $UserStub);
$this->assertEquals(true, $Timesheet->isValid( $timesheet ) );
}
创建模拟不会替换对象,以便在调用new
时创建模拟对象。它创建了一个新对象,可以扩展您现在可以传递的类。 new
是可测试性的死亡。