Now that we have our models tested, we can go to some higher level tests. We call the code that tests our controllers functional tests, and they are stored in tests/functional.
Functional tests follow all the same conventions that unit tests do and can load fixtures using the fixtures() method. Functional tests subclass Mad_Test_Functional, which itself subclasses Md_Test_Unit:
class DocumentsControllerTest extends Mad_Test_Functional
{
public function testIndex()
{
}
}
Functional tests have many features that we can use to test that our controllers and views are working properly. We want to test aspects like:
The Functional tests can send a test request to the application and receive the response:
This requires our tests to have both the request and response objects defined in our setUp() method. This is already generated within the stub file when creating new controllers with script/generate:
class DocumentsControllerTest extends Mad_Test_Functional
{
public function setUp()
{
$this->request = new Mad_Controller_Request_Mock();
$this->response = new Mad_Controller_Response_Mock();
}
}
Requests can be simulated using few different methods.
get() performs a simulated HTTP GET request to an action of the controller:
public function testIndexPage2()
{
$this->get('index', array('page' => '2'));
}
In the example above, a GET request is made to the index action of the controller being tested. The params are also set for page 2.
post() performs a simulated HTTP POST request to an action of the controller:
public function testEditSavesDocument()
{
$this->post('edit', array('id' => '123'));
}
In the example above, a POST request is made to the edit action of the controller being tested. The params are also set for ID 2.
followRedirect() will do an HTTP GET on the redirect location in a 300 level response:
public function testSomething()
{
// index action performs redirectTo(array('action' => 'binder'));
$this->get('index');
// this will perform another GET to follow the redirect
$this->followRedirect();
}
recognize() doesn’t actually perform a request, but will evaluate the routing and instantiate the correct controller for the reqeuest. This is a good way to test out that routing data is working correctly:
public function testSomething()
{
$this->recognize('/explore/folder/123');
}
Before a request is made, you can modify the request data to your heart’s content. Some things that will probably commonly be changed are: Cookies, Session Data, Flash Data, etc. This can help set the environment for your test:
public function testSomething()
{
$this->request->setCookie('FOLDERID', '123');
$this->request->setRemoteIp('172.17.118.5');
$this->request->setIsAjax(true);
$this->get('/explore/folder');
}
After a request has been performed, we have access to a valid response object. Most of the time assertions done on the response will be done through our custom assertion methods mentioned below, but one handy option you have for debugging your tests is to print out the response body:
public function testIndexBody()
{
$this->get('index');
echo $this->response;
}
We also have access to some new data that we can use to assert that the correct actions have been executed during the request.
getAssigns() will get us any template variable that was set during the action:
public function testIndexAssignsPageId()
{
$this->get('index');
$pageId = $this->getAssigns('PAGE_ID');
}
getCookie() will get us any cookie data set during the action:
public function testShowSetsDocumentIdCookie()
{
$this->get('show', array('id' => 3));
$binderId = $this->getCookie('BINDER_ID');
}
getSession() will get us any session data set during the action:
public function testShowSetsSelectedFolderInSession()
{
$this->get('show');
$folder = $this->getCookie('FOLDER_ID');
}
getFlash() will get us any flash data set during the action:
public function testLoginFlashesSuccessMessage()
{
$this->get('login');
$msg = $this->getFlash('SUCCESS_MSG');
}
There are many assertions available to the functional tests. These will help evaluate that the request/response gets executed correctly.
assertRouting(): asserts that the URL given to recognize() set the given params correctly:
public function testSomething()
{
$this->recognize('/explore/binder/123');
// assert that the params['id'] was set correctly from the url
$this->assertRouting('id' => '123');
}
assertNoRouting(): does the exact opposite of assertRouting(). It makes sure that the URL given to recognize() was not routed correctly:
public function testSomething()
{
$this->recognize('/explore/binder/asdf');
$this->assertNoRouting();
}
assertAction(): asserts that the given action/controller were executed during the request:
public function testSomething()
{
$this->recognize('index');
$this->assertAction('index', 'DocumentsController');
}
assertAssigns(): asserts that the given variable was assigned to the given value:
public function testSomething()
{
$this->get('binder', array('id' => 123));
$this->assertAssigns('PAGE_ID', 'binderExplore');
}
assertAssignsCookie(): asserts that the given cookie was assigned to the given value:
public function testSomething()
{
$this->get('binder', array('id' => 123));
$this->assertAssignsCookie('COOKIE_NAME', 'cookie value');
}
assertAssignsSession(): asserts that the given session was assigned to the given value:
public function testSomething()
{
$this->get('binder', array('id' => '123');
$this->assertAssignsSession('SESSION_NAME', 'session value');
}
assertAssignsFlash(): asserts that the given flash was assigned to the given value:
public function testSomething()
{
$this->get('binder', array('id' => '123');
$this->assertAssignsFlash('FLASH_NAME', 'flash value');
}
assertResponse(): asserts that the response was successful, redirected, missing, an error, or a specific HTTP code:
public function testSomething()
{
$this->get('binder', array('id' => '123');
$this->assertResponse('success');
$this->get('index');
$this->assertResponse('redirect'); // 302
$this->get('missing_action');
$this->assertResponse('missing'); // 404
$this->get('moved_action');
$this->assertResponse(301); // 301
}
assertRedirectedTo(): assert that the response is a redirect to the given URL:
public function testSomething()
{
$this->get('index');
$this->assertRedirectedTo(array('action' => 'binder'));
}
assertResponseContains(): assert that the given string/regexp is contained in the response body:
public function testSomething()
{
$this->get('index');
$this->assertResponseContains('Documents');
$this->assertResponseContains('/[0-9]{1,} Documents/');
}
Another assertion, assertResponseDoesNotContain(), asserts that the response does not contain the content.
assertSelect(): assert that we find an HTML tag that matches the given CSS selector and options. This assertion is probably the one you’ll use the most during your testing.
The syntax of assertSelect is very simple if you know CSS selector syntax:
We can also do combinations of these options such as:
The second argument determines what we’re matching in the content or number of tags. It can be one 4 options:
There is an element with the id “binder_1” with the content “Test Foo”:
$this->assertSelect('#binder_1', "Test Foo");
There is not an element with the id “binder_1” and the content “Test Foo”:
$this->assertSelect('#binder_1', "Test Foo", false);
The “#binder_foo” id exists:
$this->assertSelect('#binder_foo');
$this->assertSelect('#binder_foo', true);
The “#binder_foo” id DOES NOT exist:
$this->assertSelect('#binder_foo', false);
There are 10 div elements with the class folder:
$this->assertSelect('div.folder', 10);
There are more than 2, less than 10 li elements:
$this->assertSelect('ul > li', array('>' => 2, '<' => 10));