Unit Testing with ``paste.fixture``
++++++++++++++++++++++++++++++++++++

Pylons provides powerful unit testing capabilities for your web application utilizing `paste.fixture <http://pythonpaste.org/testing-applications.html#the-tests-themselves>`_ to emulate requests to your web application. You can then ensure that the response was handled appropriately and that the controller set things up properly.

To run the test suite for your web application, Pylons utilizes the `nose <http://somethingaboutorange.com/mrl/projects/nose/>`_ test runner/discovery package. Running ``nosetests`` in your project directory will run all the tests you create in the tests directory. If you don't have nose installed on your system, it can be installed via setuptools with::

    easy_install -U nose

**Note:** ``paste.fixture`` is currently incompatible with Windows, we are working on remedying this.

Testing Pylons Objects
======================

Pylons will provide several additional attributes for the ``paste.fixture`` response object that let you access various objects that were created during the web request:

``session``
    Session object
``request``
    Request object
``m``
    Myghty object
``comp_calls``
    List of component calls, including any ``scomp``, ``comp``, and ``subexec`` thats executed during the request. Each item of the list is a dict with the keys:
    
    ``comp_type``
        The type of component call, i.e. ``scomp``, ``comp``, or ``subexec``
    ``template``
        Template name that was called, i.e. ``/some/template.myt``
    ``params``
        Dict of keyword args that the component was called with

To use them, merely access the attributes of the response *after* you've used a get/post command::

    response = app.get('/some/url')
    assert response.session['var'] == 4

Example: Testing a Controller
=============================

First let's create a new project and controller for this example::

    paster create --template=pylons TestExample
    cd TestExample
    paster controller comments

You'll see that it creates two files when you create a controller. The stub controller, and a test for it under ``testexample/tests/functional/``. 

Modify the ``testexample/controllers/comments.py`` file so it looks like this::

    from testexample.lib.base import *

    class CommentsController(BaseController):
        def index(self):
            m.write('Basic output')

        def sess(self):
            session['name'] = 'Joe Smith'
            session.save()
            m.write('Saved a session')

Then write a basic set of tests to ensure that the controller actions are functioning properly, modify ``testexample/tests/functions/test_comments.py`` to match the following::

    from testexample.tests import *

    class TestCommentsController(TestController):
        def test_index(self):
            response = self.app.get(url_for(controller='/comments'))
            assert 'Basic output' in response

        def test_sess(self):
            response = self.app.get(url_for(controller='/comments', action='sess'))
            assert response.session['name'] == 'Joe Smith'
            assert 'Saved a session' in response

Run ``nosetests`` in your main project directory and you should see them all pass::

    ..
    ----------------------------------------------------------------------
    Ran 2 tests in 2.999s

    OK

Unfortunately, nosetests (as of 0.8.6 at least) cannot provide detailed information about the results of an assertion should it fail when you use this format for testing. For example, add the following test to the ``test_sess`` function::

    assert response.session.has_key('address') == True

When you run ``nosetests`` you will get the following, not-very-helpful response::

    .F
    ======================================================================
    FAIL: test_sess (testexample.tests.functional.test_comments.TestCommentsController)
    ----------------------------------------------------------------------
    Traceback (most recent call last):
      File "~/TestExample/testexample/tests/functional/test_comments.py", line 12, in test_sess
        assert response.session.has_key('address') == True
    AssertionError: 


    ----------------------------------------------------------------------
    Ran 2 tests in 1.417s

    FAILED (failures=1)

Since our TestController we inherited from is a standard Python unit testing controller we can use the assertEqual statement which will provide more information. The new test line looks like this::

    self.assertEqual(response.session.has_key('address'), True)

Which provides the more useful failure message::

    .F
    ======================================================================
    FAIL: test_sess (testexample.tests.functional.test_comments.TestCommentsController)
    ----------------------------------------------------------------------
    Traceback (most recent call last):
      File "~/TestExample/testexample/tests/functional/test_comments.py", line 12, in test_sess
        self.assertEqual(response.session.has_key('address'), True)
    AssertionError: False != True
    
For more details on running tests using get/post, and testing the response, headers, etc., see the Paste document on web application testing:

`Testing Application with Paste, the Tests <http://pythonpaste.org/testing-applications.html#the-tests-themselves>`_
