# Examples

Knowing how to setup your testing infrastructure and how to make assertions is only half the battle; now it's time to start looking at some actual testing scenarios to see how you can leverage them.

## Testing a UserController

Let's consider a standard task for a website: authenticating and registering users. In our example, we'll define a UserController for handling this, and have the following requirements:

- If a user is not authenticated, they will always be redirected to the login page of the controller, regardless of the action specified.

- The login form page will show both the login form and the registration form.

- Providing invalid credentials should result in returning to the login form.

- Valid credentials should result in redirecting to the user profile page.

- The profile page should be customized to contain the user's username.

- Authenticated users who visit the login page should be redirected to their profile page.

- On logout, a user should be redirected to the login page.

- With invalid data, registration should fail.

We could, and should define further tests, but these will do for now.

For our application, we will define a plugin, 'Initialize', that runs at `routeStartup()`. This allows us to encapsulate our bootstrap in an OOP interface, which also provides an easy way to provide a callback. Let's look at the basics of this class first:

    class Bugapp_Plugin_Initialize extends Zend_Controller_Plugin_Abstract
    {
        /**
         * @var Zend_Config
         */
        protected static $_config;

        /**
         * @var string Current environment
         */
        protected $_env;

        /**
         * @var Zend_Controller_Front
         */
        protected $_front;

        /**
         * @var string Path to application root
         */
        protected $_root;

        /**
         * Constructor
         *
         * Initialize environment, root path, and configuration.
         *
         * @param  string $env
         * @param  string|null $root
         * @return void
         */
        public function __construct($env, $root = null)
        {
            $this->_setEnv($env);
            if (null === $root) {
                $root = realpath(dirname(__FILE__) . '/../../../');
            }
            $this->_root = $root;

            $this->initPhpConfig();

            $this->_front = Zend_Controller_Front::getInstance();
        }

        /**
         * Route startup
         *
         * @return void
         */
        public function routeStartup(Zend_Controller_Request_Abstract $request)
        {
            $this->initDb();
            $this->initHelpers();
            $this->initView();
            $this->initPlugins();
            $this->initRoutes();
            $this->initControllers();
        }

        // definition of methods would follow...
    }

This allows us to create a bootstrap callback like the following:

    class UserControllerTest extends Zend_Test_PHPUnit_ControllerTestCase
    {
        public function appBootstrap()
        {
            $controller = $this->getFrontController();
            $controller->registerPlugin(
                new Bugapp_Plugin_Initialize('development')
            );
        }

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

        // ...
    }

Once we have that in place, we can write our tests. However, what about those tests that require a user is logged in? The easy solution is to use our application logic to do so... and fudge a little by using the `resetRequest()` and `resetResponse()` methods, which will allow us to dispatch another request.

    class UserControllerTest extends Zend_Test_PHPUnit_ControllerTestCase
    {
        // ...

        public function loginUser($user, $password)
        {
            $this->request->setMethod('POST')
                          ->setPost(array(
                              'username' => $user,
                              'password' => $password,
                          ));
            $this->dispatch('/user/login');
            $this->assertRedirectTo('/user/view');

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

            $this->request->setPost(array());

            // ...
        }

        // ...
    }

Now let's write tests:

    class UserControllerTest extends Zend_Test_PHPUnit_ControllerTestCase
    {
        // ...

        public function testCallWithoutActionShouldPullFromIndexAction()
        {
            $this->dispatch('/user');
            $this->assertController('user');
            $this->assertAction('index');
        }

        public function testLoginFormShouldContainLoginAndRegistrationForms()
        {
            $this->dispatch('/user');
            $this->assertQueryCount('form', 2);
        }

        public function testInvalidCredentialsShouldResultInRedisplayOfLoginForm()
        {
            $request = $this->getRequest();
            $request->setMethod('POST')
                    ->setPost(array(
                        'username' => 'bogus',
                        'password' => 'reallyReallyBogus',
                    ));
            $this->dispatch('/user/login');
            $this->assertNotRedirect();
            $this->assertQuery('form');
        }

        public function testValidLoginShouldRedirectToProfilePage()
        {
            $this->loginUser('foobar', 'foobar');
        }

        public function testAuthenticatedUserShouldHaveCustomizedProfilePage()
        {
            $this->loginUser('foobar', 'foobar');
            $this->request->setMethod('GET');
            $this->dispatch('/user/view');
            $this->assertNotRedirect();
            $this->assertQueryContentContains('h2', 'foobar');
        }

        public function
            testAuthenticatedUsersShouldBeRedirectedToProfileWhenVisitingLogin()
        {
            $this->loginUser('foobar', 'foobar');
            $this->request->setMethod('GET');
            $this->dispatch('/user');
            $this->assertRedirectTo('/user/view');
        }

        public function testUserShouldRedirectToLoginPageOnLogout()
        {
            $this->loginUser('foobar', 'foobar');
            $this->request->setMethod('GET');
            $this->dispatch('/user/logout');
            $this->assertRedirectTo('/user');
        }

        public function testRegistrationShouldFailWithInvalidData()
        {
            $data = array(
                'username' => 'This will not work',
                'email'    => 'this is an invalid email',
                'password' => 'Th1s!s!nv@l1d',
                'passwordVerification' => 'wrong!',
            );
            $request = $this->getRequest();
            $request->setMethod('POST')
                    ->setPost($data);
            $this->dispatch('/user/register');
            $this->assertNotRedirect();
            $this->assertQuery('form .errors');
        }
    }

Notice that these are terse, and, for the most part, don't look for actual content. Instead, they look for artifacts within the response -- response codes and headers, and DOM nodes. This allows you to verify that the structure is as expected -- preventing your tests from choking every time new content is added to the site.

Also notice that we use the structure of the document in our tests. For instance, in the final test, we look for a form that has a node with the class of "errors"; this allows us to test merely for the presence of form validation errors, and not worry about what specific errors might have been thrown.

This application may utilize a database. If so, you will probably need some scaffolding to ensure that the database is in a pristine, testable configuration at the beginning of each test. PHPUnit already provides functionality for doing so; read about it in the PHPUnit documentation. We recommend using a separate database for testing versus production, and in particular recommend using either a SQLite file or in-memory database, as both options perform very well, do not require a separate server, and can utilize most SQL syntax.