user2511309
user2511309

Reputation: 309

CakePHP controller unit test

I want to test the add action of a controller method. I want to verify to result of a save action. So basically I want to send a post to the action and verify the result in my test by using a find after the request is finished. $this->testAction() doesn’t seem to be the right way to do this (see comments in the code below). How should this be done?

Controller code is something like:

Public function add() {
  .....
  if ($this->request->is('post') && isset($this->request->data['InvoiceEntry'])) {
    ....
    $this->request->data = $this->__someMethod($this->request->data);
    if ($this->Invoice->saveAssociated($this->request->data)) {
      ....
      $this->redirect(array('action' => 'index'));
    }
  .....
}

Test code:

public function testAdd() {
   $data = array('Invoice' => array(...), 'InvoiceEntry' => array(....));

    // Method 1
    $this->testAction('/invoices/add/', array(
        'method' => 'post',
        'data' => $data,
    ));
    // Unable to do find after testAction because testAction basically is the test?


    // Method 2:
    $this->controller = $this->generate('Invoices');
    $_SERVER['REQUEST_METHOD'] = 'POST';
    $this->controller->requestAction('/invoices/add',array(
        'data' => $data
    ));
   // not working because requestAction() follows the redirect in the add method
}

Upvotes: 0

Views: 1661

Answers (2)

Oldskool
Oldskool

Reputation: 34877

First of all, you should initialize your Model class in the test's setUp() method:

public function setUp() {
    parent::setUp();
    $this->Invoice = ClassRegistry::init('Invoice');
}

After that you can just use that model as you otherwise would. So after you do:

$this->testAction('/invoices/add/', array(
    'method' => 'post',
    'data' => $data,
));

You should then be able to check if it was added, for example by doing:

/*
 * Check if the last insert id is 13
 * (NOTE: This actual number depends on how your fixture looks.)
 */
$this->assertEquals(13, $this->Invoice->getLastInsertID());

Or check some content, like:

// Find the latest invoice
$invoice = $this->Invoice->find('first', array(
    'order' => array('id' => 'desc')
));

/*
 * Verify the data matches your $data array
 * e.g. if the customer_id of this invoice is 1
 * (this depends on what is actually in $data)
 */
$this->assertEquals(1, $invoice['Invoice']['customer_id']);

Finally, don't forget to destruct the Model instance in your tearDown() method:

public function tearDown() {
    unset($this->Invoice);
    parent::tearDown();
}

Upvotes: 1

chadrien
chadrien

Reputation: 498

You should be able to do anything after a testAction. It's not the test itself, it just runs the code in the action. What happens if you do a Model::find() after your testAction? Or even a debug('foo');exit;? This should be executed.

By the way, in your controller you should use return in front of a redirect. It's recommended by CakePHP because has you terminal is not a browser (I assume you test with the command line, not the webroot/test.php, and if you do stop and use the terminal as testing via your browser can lead to incoherent tests because of cookies/sessions) and won't follow the redirect, therefor will execute the code that may be after the Controller::redirect(), and you don't want that :)

Upvotes: 1

Related Questions