单元测试Zend控制器并模拟执行的某些操作

5
我正在为我的控制器编写一些单元测试(PHPUnit 3.6),并希望验证是否正确触发了特定的操作等。这很容易。然而,一些控制器还通过模型执行某些不必要的操作,例如将记录插入到数据库中。我知道需要模拟这些操作,但是不清楚如何做。以下是一个简化版的示例控制器:

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

}

Note, all I want to do is verify that the correct controller and action have been dispatched but do not want the record actually inserted. Likewise I have equivalents for delete etc.. I don't want records actually deleted.

What actually needs mocking here? The competition Model, the database adapter, or the model factory, or all three? How do I inject these? I have tried (again cut down for brevity):

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);
}

}

It was my understanding that PHPUnit magically substituted any references to the originals with the mocks so that when the dispatch was called the fake mocks are used in their place. This isn't happening. Can someone please clarify how this is achieved?

2个回答

4
看起来你的模拟设置正确。事实上,在看到你的问题并进行了一些研究之前,我并不知道你可以从模拟中返回模拟。
这里发生的情况是,你需要使方法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();    
}

谢谢,是的,本质上我使用配置文件模拟了工厂,以便单元测试不使用真实对象。 - John Royal

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接