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:
API Reference:
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()