问题
I am writing a CakePHP Unit Test for one of my Controllers. The Controller has several calls to the AuthComponent::user()
method, to read data of the currently logged in user. There are 3 usages:
AuthComponent::user()
(no params, fetches the entire array)AuthComponent::user('id')
(fetches the user id)AuthComponent::user('name')
(fetches the username)
I have tried two ways of mocking the AuthComponent in my test:
// Mock the Controller and the Components
$this->controller = $this->generate('Accounts', array(
'components' => array(
'Session', 'Auth' => array('user'), 'Acl'
)
));
// Method 1, write the entire user array
$this->controller->Auth->staticExpects($this->any())->method('user')
->will($this->returnValue(array(
'id' => 2,
'username' => 'admin',
'group_id' => 1
)));
// Method 2, specifically mock the AuthComponent::user('id') method
$this->controller->Auth->staticExpects($this->any())->method('user')
->with('id')
->will($this->returnValue(2));
These methods do not work for me, though. Method 1 doesn't appear to do anything at all, a save operation in my controller that uses the currently logged in user's id returns null, so the value is not properly set/obtained.
Method 2 seems to work, but is too broad, it also tries to bind itself to the AuthComponent::user()
call (the one without params) and it fails with the error:
Expectation failed for method name is equal to when invoked zero or more times Parameter 0 for invocation AuthComponent::user(null) does not match expected value. Failed asserting that null matches expected 'id'.
How can I get proper mocks for the AuthComponent so all the fields/variables can be obtained?
回答1:
This is how I do it. Note, in this code, I'm using 'Employee' as my user model, but it should be easy to change.
I have an AppControllerTest.php superclass, which returns a callback for the 'user' method. The callback handles the case with and without params. The _generateMockWithAuthUserId
is what you're after - but read it all. There's a couple of other things worth taking note of, like the testPlaceholder. Here's my whole class:
<?php
App::uses('Employee', 'Model');
/**
* EmployeeNotesController Test Case
* Holds common Fixture ID's and mocks for controllers
*/
class AppControllerTest extends ControllerTestCase {
public $authUserId;
public $authUser;
/**
* setUp method
*
* @return void
*/
public function setUp() {
parent::setUp();
$this->Employee = ClassRegistry::init('Employee');
}
/**
* tearDown method
*
* @return void
*/
public function tearDown() {
unset($this->Employee);
parent::tearDown();
}
public function testPlaceholder() {
// This just here so we don't get "Failed - no tests found in class AppControllerTest"
$this->assertTrue(true);
}
protected function _generateMockWithAuthUserId($contollerName, $employeeId) {
$this->authUserId = $employeeId;
$this->authUser = $this->Employee->findById($this->authUserId);
$this->controller = $this->generate($contollerName, array(
'methods' => array(
'_tryRememberMeLogin',
'_checkSignUpProgress'
),
'components' => array(
'Auth' => array(
'user',
'loggedIn',
),
'Security' => array(
'_validateCsrf',
),
'Session',
)
));
$this->controller->Auth
->expects($this->any())
->method('loggedIn')
->will($this->returnValue(true));
$this->controller->Auth
->staticExpects($this->any())
->method('user')
->will($this->returnCallback(array($this, 'authUserCallback')));
}
public function authUserCallback($param) {
if (empty($param)) {
return $this->authUser['Employee'];
} else {
return $this->authUser['Employee'][$param];
}
}
}
Then, my controller test cases inherit from that class:
require_once dirname(__FILE__) . DS . 'AppControllerTest.php';
class EmployeeNotesControllerTestCase extends AppControllerTest {
// Tests go here
And when you want to mock the auth component in a test, you call
$this->_generateMockWithAuthUserId('EmployeeNotes', $authUserId);
Where 'EmployeeNotes' would be the name of your controller, and $authUserId the ID of a user in the test database.
来源:https://stackoverflow.com/questions/19455540/mocking-an-authed-user-in-cakephp