Skip to content

Testing

Mark edited this page Apr 12, 2014 · 27 revisions

Testing - best pratices with Cake2.x and PHPUnit3.7

Testing Controllers

In general it is best to only use testAction() once per test. My convention for the test method name is to use

"test" + "action name including prefix" + "method (if not default one)" + "optionally other pieces"

The following tests - using the Tools plugin MyControllerTestCase class - will per default use GET method unless specified otherwise:

/**
 * testAdminAdd()
 *
 * @return void
 */
public function testAdminAdd() {
	$url = Router::url(array('admin' => true, 'controller' => 'users', 'action' => 'add'));
	$options = array(
		'return' => 'contents'
	);
	$result = $this->testAction($url, $options);
	$this->assertNotEmpty($result);
}

/**
 * testAdminAddPost()
 *
 * @return void
 */
public function testAdminAddPost() {
	$url = Router::url(array('admin' => true, 'controller' => 'users', 'action' => 'add'));
	$options = array(
		'method' => 'post',
		'return' => 'contents',
		'data' => array(
			'User' => array(
				'name' => 'test'
			)
		)
	);
	$result = $this->testAction($url, $options);
	$this->assertNull($result);

	$url = Router::url(array('admin' => true, 'controller' => 'users', 'action' => 'index'));
	$this->assertTextContains($url, $this->headers['Location']);
}

Note how I use dynamic URLs here to avoid routing changes affect all controller tests. So these tests effectively test the same URL as the frontend.

So if the following actions are available in a controller, basic tests should probably be added for

  • index (GET)
  • view (GET)
  • add (GET + POST)
  • edit (GET + POST)
  • delete (POST)

and their admin counterparts, if applicable.

Using controller tests also tests the views to the actions. Which is a nice side effect. Any warning/error popping up there will then also be visible right away.

For plugin controllers to be testable unfortunately you always need to use $uses, even if it wasn't necessary due to conventions. So for your Tools.TinyUrls Controller you would need

public $uses = array('Tools.TinyUrl');

Otherwise it would always try to look for the model/fixture in your app dir, which eventually always fails.

Note: Do not forget to use --stderr when testing in CLI. Otherwise all tests that use the session (and which work fine in webtest runner) will fail:

cake test app AllController --stderr

When testing controllers that need authentication (like admin actions), you can either mock the Auth component or simply write to the session to log in a specific user/role in setUp() or before using testAction():

CakeSession::write('Auth.User', array(
	'username' => 'foo',
	'role_id' => $roleId,
	'id' => 1,
));

Don't forget to CakeSession::delete('Auth.User'); afterwards to avoid it affecting other tests. It is also a good practice then to use this to clear the session prior to any test using setUp().

Testing Shells

TODO

Testing Models

Make sure you are using ClassRegistry::init() to init your models. This way they will automatically have the fixtures and db test config included.

Testing Helpers

Make sure you include a View:

App::uses('View', 'View');

class LoremHelperTest extends CakeTestCase {

	public $Lorem;

	public function setUp() {
		parent::setUp();

		$this->Lorem = new LoremHelper(new View(null));
	}

}

This is necessary when the helper needs any View attributes.

Example: LoremHelperTest.php

In some cases it might also be necessary to pass a Controller or even a request object:

$this->SomeHelper = new SomeHelper(new View(new Controller(new CakeRequest(null, false))));

Test Coverage

If you want to generate a HTML overview of all your locale test coverage:

cake test app AllApp --stderr --log-junit tmp/coverage/unitreport.xml --coverage-html tmp/coverage --coverage-clover tmp/coverage/coverage.xml

The report index.html will be in your /tmp/coverage folder.

Clone this wiki locally