单元测试遗留代码

时间:2014-02-21 14:02:54

标签: zend-framework phpunit

我是单位测试的新手,并尝试对使用表单的旧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 ) );
  }

我的数据提供者位于单独的文件中。

它在命令行终止,没有输出,我有点难过。任何帮助将不胜感激。

1 个答案:

答案 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是可测试性的死亡。