0

I am using CakePHP 2.4

I have a Model class called Storybook.

There is a method called createDraft and its code looks like this:

    public function createDraft($data) {
        $data['Storybook']['author']    = AuthComponent::user('id');

When I write a test script using the CakePHP Tests, I have a problem instantiating the user data inside AuthComponent.

My test script looks like this:

<?php
App::uses('Storybook', 'Model');
class StorybookTestCase extends CakeTestCase {

/**
 * Fixtures
 *
 * @var array
 */
    public $fixtures = array(...
    );

/**
 * setUp method
 *
 * @return void
 */
    public function setUp() {
        parent::setUp();
        $this->Storybook    = ClassRegistry::init('Storybook');
...
...


/**
 * GIVEN we have only a title
 * WHEN we call createDraft
 * THEN we have a new draft
 *
 * @return void
 */
     public function testCreateDraftSuccessfully() {    
        // GIVEN only a title
        $data = array('Storybook' => array('title' => 'some title'));
        // WHEN we call the createDraft
        $actualResult = $this->Storybook->createDraft($data);
 ....

The test failed at createDraft method.

I cannot fill in the AuthComponent with user data at the setUp method since AuthComponent login method is not static.

Please advise.

4

1 回答 1

3

The steps I figured out after some googling and asking #cakephp IRC channel:

1) Ensure you are using at least CakePHP 2.3

2) create the following protected function in the AppModel:

protected function _getUser($key = null) {
    return AuthComponent::user($key);
}

3) ensure that AuthComponent is actually used inside the AppModel:

//type this just below App::uses('Model', 'Model');
App::uses('AuthComponent', 'Controller/Component');

4) Change the Storybook model to use _getUser instead of AuthComponent::user

    public function createDraft($data) {
        $data['Storybook']['author']    = $this->_getUser('id');

5) Add the following into the test method courtesy of this documentation:

public function testCreateDraftSuccessfully() {    

        // MOCK the _getUser method to always return 23 because we always expect that
        $this->Storybook = $this->getMockForModel('Storybook', array('_getUser'));
        $this->Storybook->expects($this->once())
        ->method('_getUser')
        ->will($this->returnValue(23));

    // GIVEN only a title
    $data = array('Storybook' => array('title' => 'some title'));

That will help you pass the test.

UPDATE

If the method you are mocking may return different results depending on different parameters, you need to have another public function that will act as a callback for the mocked method.

Alternative to step 5) Add the following function if you expect to always return 23 if the parameter is 'id' and 1 if the parameter is 'group_id'

public function getUserCallBack($field) {
        if ($field == 'id') {
            return 23;
        }
        if ($field == 'group_id') {
            return 1;
        }
    }

6) Change the test method to:

public function testCreateDraftSuccessfully() {    

        // MOCK the _getUser method to always return 23 for _getUser('id') and 1 for _getUser('group_id')
        $this->Storybook = $this->getMockForModel('Storybook', array('_getUser'));
        $this->Storybook->expects($this->once())
        ->method('_getUser')
        ->with($this->logicalOr(
            $this->equalTo('id'),
            $this->equalTo('group_id')
        ))
        ->will($this->returnCallback(array($this, 'getUserCallBack')));

    // GIVEN only a title
    $data = array('Storybook' => array('title' => 'some title'));

Useful list of matchers for PHPunit. E.g.

$this->once()
$this->never()
于 2013-10-02T10:23:32.360 回答