views:

79

answers:

1

How do I begin testing my models in a Zend Framework 1.8+ application?

Let's say I have my application set up to start testing. I have already tested a controller, so I know it works. I have all my controllers extending my ControllerTestCase.php file:

<?php
require_once 'Zend/Application.php';
require_once 'Zend/Test/PHPUnit/ControllerTestCase.php';

abstract class ControllerTestCase extends Zend_Test_PHPUnit_ControllerTestCase
{
    public $application;

    public function setUp()
    {
        $this->application = new Zend_Application(
            APPLICATION_ENV,
            APPLICATION_PATH . '/configs/application.ini'
        );

        $this->bootstrap = array($this, 'appBootstrap');
        parent::setUp();
    }

    public function appBootstrap()
    {
        $this->application->bootstrap();
    }

    public function tearDown()
    {
        Zend_Controller_Front::getInstance()->resetInstance();

        $this->resetRequest();
        $this->resetResponse();

        $this->request->setPost(array());
        $this->request->setQuery(array());
        parent::tearDown();
    }
}

But now I want to start testing my models. It seems like my ModelTestCase.php would not extend Zend_Test_PHPUnit_ControllerTestCase but rather a Zend_Test_PHPUnit_ModelTestCase, but no such class exists that I know of. How can I start testing my Zend Framework models?

+2  A: 

There is a base ControllerTestCase provided for you because there are complex steps needed to setup and tear down the environment for testing a controller. The input is a mock HTTP request, and the output is rendered HTML that you need to scrape to find expected content.

A Model is more like a plain old PHP object. There's less environment to set up. The interface is simply method calls to the object.

So I would start a TestCase class that extends PHPUnit's plain TestCase, and start by adding at least one test method (as an empty function) for each method in your Model class. You will eventually have many test methods for each method in your Model class, but creating the empty test methods is a good way to keep from forgetting some of your Model methods.

Note that a Model is not a Table -- a Model typically uses one or more Table objects. By following this pattern, you have the opportunity to create mock objects for Tables so you can run the test suite without requiring a live connection to a database.

Here's an example of setting up a mock Table object, which is hardcoded to return a synthetic data set instead of a data set from a database.

class MyModelTest extends PHPUnit_Framework_TestCase
{
  protected $_model;

  public function setUp()
  {
    $foo = $this->getMock('FooTable', array('find'));
    $foo->expects($this->any())
        ->method('find')
        ->will($this->returnValue(array("id"=>"123"));

    $this->_model = new MyModel();
    $this->_model->setFooTable($foo);
  }

  public function testCountElements()
  {
    $this->_model->get(123);
    $n = $this->_model->countElements();
    $this->assertEquals(1, $n);
  }

  public function testAsArray()
  {
    $this->_model->get(123);
    $a = $this->_model->asArray();
    $this->assertType('array', $a);
  }

  public function testAddElement()
  {
    // ...etc.
  }

  public function testGetElement()
  {
    // ...etc.
  }

}
Bill Karwin
Thank you. That is a very good explanation. I was wondering if you would mind including a little more about including/requiring/autoloading models when you are testing. I have a model that extends an abstract model. Maybe this is where mock objects come in?
Andrew
I've added some detail above showing the use of a mock Table object. Loading the classes is totally ordinary, you can do it with `require_once` or with autoloading. I think you might be making this more complex than it actually is.
Bill Karwin