单元测试Zend Controller并模拟一些执行的操作

时间:2012-03-29 16:05:27

标签: php zend-framework mocking phpunit

我正在为我的控制器编写一些单元测试(PHPUnit 3.6),并希望验证是否正在触发正确的操作等。这很容易。然而,一些控制器还通过诸如将记录插入数据库之类的不期望的模型执行某些动作。

我知道我需要嘲笑这些但不清楚如何做到这一点。采用以下示例控制器(为清晰起见减少):

public function addAction()
{
    $data = $this->getRequest()->getPost();
    $model = $this->getModelFactory()->getCompetitionModel()->insert($data);    }

}

注意,我想要做的就是验证是否已调度正确的控制器和操作但不希望实际插入记录。同样,我有删除等的等价物。我不希望记录实际删除。

这里究竟需要嘲笑什么?竞争模型,数据库适配器,或模型工厂,还是全部三个?我该如何注射这些?我试过了(为了简洁而再次减少):

public function testAddActionIsDispatched()
{
    $this->request->setMethod('POST');
    $this->request->setPost(array($data…));

            $modelMock = $this->getMockBuilder('Competition_Adder')
                 ->disableOriginalConstructor()
                 ->getMock();                


            $factoryMock = $this->getMockBuilder('ModelFactory')
                    ->disableOriginalConstructor()
                    ->getMock(); 

        // Configure the stub.
            $factoryMock->expects($this->any())
                ->method('getCompetitionModel')
                ->will($this->returnValue($modelMock));        

            $modelMock->expects($this->once())
                    ->method('insert')
                    ->will($this->returnValue(true));

            $this->dispatch('/mymodule/add/');
            $this->assertController('test');
            $this->assertAction('add');  
            $this->assertResponseCode(200);
}

}

据我所知,PHPUnit用模拟神奇地替换了对原始文件的任何引用,这样当调用调度时,伪模拟被用于它们的位置。这不会发生。有人可以澄清一下如何实现这一目标吗?

2 个答案:

答案 0 :(得分:3)

看起来您的模拟设置正确。我实际上不知道你可以从模拟中返回模拟,直到我看到你的问题并稍微研究一下。

这里发生的是你需要让方法getModelFactory()返回你的模拟工厂的一个实例。现在它只是回归真实的东西。

我不确定你的getModelFactory方法会发生什么,所以我很难说你如何覆盖它以使它返回你的模拟工厂。

但也许你不必覆盖它。在我的ZF应用程序中,我不测试控制器,但为了测试需要保存模型的东西,我只需要在配置文件中更改为测试数据库进行测试。我使用Doctrine1.2,所以我只是在setUp()方法中启动一个事务,在tearDown方法()中回滚。

我的测试数据库是完全空的,我基本上在每个测试方法中使用一些测试特定的工厂类创建必要的数据。缺点是它似乎确实使用了大量内存。我认为它在大约140次测试中达到200MB,而且并非所有这些都需要数据库访问。

我只是使用这种方法,因为它是我最容易实现的,因为我只需要更改数据库配置。如果您没有在大型项目中工作,这可能对您有用。您还可以使用内存中的sqlite对测试数据库运行测试,这应该对您有用,因为您没有在测试中测试数据库。数据刚刚插入,然后在测试结束时,它就消失了。在我的项目中,我使用MySQL测试数据库,因为我希望它尽可能接近生产中的数据。

示例(您可能没有使用Doctrine。我只是说明了如何使用事务和回滚来保持测试数据库处于一致状态):

public function setUp()
{
    $this->bootstrap = new Zend_Application(
        APPLICATION_ENV, APPLICATION_CONFIG);       
    parent::setUp();
    $bootstrap = $this->bootstrap->getBootstrap();

    $this->_conn = Doctrine_Manager::connection();
    $this->_conn->beginTransaction();
}

public function tearDown()
{
    $this->_conn->rollback();        
    $this->_conn->close();    
}

答案 1 :(得分:0)

是的,请查看上一个答案。在所有情况下我都同意。但是MySQL和Zend_Db无法实现同样的功能。那是因为Zend_Db没有嵌套事务。 所以你唯一能做的就是使用测试数据库并在每次测试后重建它。

查看how it is done via Codeception testing framework with Zend Framework and Db module