# Unit Testing Your Application

Testing is an incredibly important part of the application development process. The Cement framework provides some simple helpers and shortcuts for executing basic tests of your application. Please note that cement.utils.test does require the ‘nose’ package. That said, using cement.utils.test is not required to test a Cement based application. It is merely here for convenience, and is used by Cement when performing its own Nose tests.

For more information on testing, please see the following:

> - UnitTest
> - Nose
> - Coverage

API Reference:

> - Cement Testing Utility

## An Example Test Case

The following outlines a basic test case using the cement.utils.test module.

    from cement.utils import test
    from myapp.cli.main import MyApp

    class MyTestCase(test.CementTestCase):
        app_class = MyApp

        def setUp(self):
            super(MyTestCase, self).setUp()

            # Create a default application for the test functions to use.
            # Note that some tests may require you to perform this in the
            # test function in order to alter functionality.  That's perfectly
            # fine, this is only here for convenience.
            self.app = MyApp(argv=[], config_files=[])

        def test_myapp(self):
            with self.app as app:

                # Perform basic assertion checks.  You can do this anywhere
                # in the test function, depending on what the assertion is
                # checking.
                self.ok(app.config.has_key('myapp', 'debug'))
                self.eq(app.config.get('myapp', 'debug'), False)

                # Run the applicaion, if necessary
                app.run()

                # Test the last rendered output (if app.render was used)
                data, output = app.get_last_rendered()
                self.eq(data, {'foo':'bar'})
                self.eq(output, 'some rendered output text')

        @test.raises(Exception)
        def test_exception(self):
            try:
                # Perform tests that intentionally cause an exception.  The
                # test passes only if the exception is raised.
                raise Exception('test')
            except Exception as e:
                # Do further checks to ensure the proper exception was raised
                self.eq(e.args[0], 'Some Exception Message')

                # Finally, call raise again which re-raises the exception that
                # we just caught.  This completes our test (to actually
                # verify that the exception was raised)
                raise

## Cement Testing Caveats

In general, testing Cement applications should be no different than testing anything else in Python. That said, the following are some things to keep in mind.

Command Line Arguments

Never rely on `sys.argv` for command line arguments. The `CementApp()` class accepts the `argv` keyword argument allowing you to pass the arguments that you would like to test for. Using `sys.argv` will cause issues with the calling script (i.e. `nosetests`, etc) and other issues. Always pass `argv` to `CementApp()` in tests.

Config Files

It is recommended to always set your apps `config_files` setting to an empty list, or to something relative to your current working directory. Using default config files settings while testing will introduce unexpected results. For example, if a `~/myapp.conf` user configuration exists it can alter the runtime of your application in a way that might cause tests to fail.

Making Things Easy

The easiest way to accomplish the above is by sub-classing your `CementApp` into a special ‘testing’ version. For example:

    from cement.utils import test
    from myapp.cli.main import MyApp

    class MyTestApp(MyApp):
        class Meta:
            argv = []
            config_files = []

    class MyTestCase(test.CementTestCase):
        app_class = MyTestApp

        def test_myapp_default(self):
            with self.app as app:
                app.run()

        def test_myapp_foo(self):
            with MyTestApp(argv=['--foo', 'bar']) as app:
                app.run()