1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
|
Running Initialization Code Before the Test Run
-----------------------------------------------
Many applications, especially those using web frameworks like Pylons_
or Django_, can't be tested without first being configured or
otherwise initialized. Plugins can fulfill this requirement by
implementing :meth:`begin() <nose.plugins.base.IPluginInterface.begin>`.
In this example, we'll use a very simple example: a widget class that
can't be tested without a configuration.
Here's the widget class. It's configured at the class or instance
level by setting the ``cfg`` attribute to a dictionary.
>>> class ConfigurableWidget(object):
... cfg = None
... def can_frobnicate(self):
... return self.cfg.get('can_frobnicate', True)
... def likes_cheese(self):
... return self.cfg.get('likes_cheese', True)
The tests verify that the widget's methods can be called without
raising any exceptions.
>>> import unittest
>>> class TestConfigurableWidget(unittest.TestCase):
... longMessage = False
... def setUp(self):
... self.widget = ConfigurableWidget()
... def test_can_frobnicate(self):
... """Widgets can frobnicate (or not)"""
... self.widget.can_frobnicate()
... def test_likes_cheese(self):
... """Widgets might like cheese"""
... self.widget.likes_cheese()
... def shortDescription(self): # 2.7 compat
... try:
... doc = self._testMethodDoc
... except AttributeError:
... # 2.4 compat
... doc = self._TestCase__testMethodDoc
... return doc and doc.split("\n")[0].strip() or None
The tests are bundled into a suite that we can pass to the test runner.
>>> def suite():
... return unittest.TestSuite([
... TestConfigurableWidget('test_can_frobnicate'),
... TestConfigurableWidget('test_likes_cheese')])
When we run tests without first configuring the ConfigurableWidget,
the tests fail.
.. Note ::
The function :func:`nose.plugins.plugintest.run` reformats test result
output to remove timings, which will vary from run to run, and
redirects the output to stdout.
>>> from nose.plugins.plugintest import run_buffered as run
..
>>> argv = [__file__, '-v']
>>> run(argv=argv, suite=suite()) # doctest: +REPORT_NDIFF
Widgets can frobnicate (or not) ... ERROR
Widgets might like cheese ... ERROR
<BLANKLINE>
======================================================================
ERROR: Widgets can frobnicate (or not)
----------------------------------------------------------------------
Traceback (most recent call last):
...
AttributeError: 'NoneType' object has no attribute 'get'
<BLANKLINE>
======================================================================
ERROR: Widgets might like cheese
----------------------------------------------------------------------
Traceback (most recent call last):
...
AttributeError: 'NoneType' object has no attribute 'get'
<BLANKLINE>
----------------------------------------------------------------------
Ran 2 tests in ...s
<BLANKLINE>
FAILED (errors=2)
To configure the widget system before running tests, write a plugin
that implements :meth:`begin() <nose.plugins.base.IPluginInterface.begin>`
and initializes the system with a hard-coded configuration. (Later, we'll
write a better plugin that accepts a command-line argument specifying the
configuration file.)
>>> from nose.plugins import Plugin
>>> class ConfiguringPlugin(Plugin):
... enabled = True
... def configure(self, options, conf):
... pass # always on
... def begin(self):
... ConfigurableWidget.cfg = {}
Now configure and execute a new test run using the plugin, which will
inject the hard-coded configuration.
>>> run(argv=argv, suite=suite(),
... plugins=[ConfiguringPlugin()]) # doctest: +REPORT_NDIFF
Widgets can frobnicate (or not) ... ok
Widgets might like cheese ... ok
<BLANKLINE>
----------------------------------------------------------------------
Ran 2 tests in ...s
<BLANKLINE>
OK
This time the tests pass, because the widget class is configured.
But the ConfiguringPlugin is pretty lame -- the configuration it
installs is hard coded. A better plugin would allow the user to
specify a configuration file on the command line:
>>> class BetterConfiguringPlugin(Plugin):
... def options(self, parser, env={}):
... parser.add_option('--widget-config', action='store',
... dest='widget_config', default=None,
... help='Specify path to widget config file')
... def configure(self, options, conf):
... if options.widget_config:
... self.load_config(options.widget_config)
... self.enabled = True
... def begin(self):
... ConfigurableWidget.cfg = self.cfg
... def load_config(self, path):
... from ConfigParser import ConfigParser
... p = ConfigParser()
... p.read([path])
... self.cfg = dict(p.items('DEFAULT'))
To use the plugin, we need a config file.
>>> import os
>>> cfg_path = os.path.join(os.path.dirname(__file__), 'example.cfg')
>>> cfg_file = open(cfg_path, 'w')
>>> bytes = cfg_file.write("""\
... [DEFAULT]
... can_frobnicate = 1
... likes_cheese = 0
... """)
>>> cfg_file.close()
Now we can execute a test run using that configuration, after first
resetting the widget system to an unconfigured state.
>>> ConfigurableWidget.cfg = None
>>> argv = [__file__, '-v', '--widget-config', cfg_path]
>>> run(argv=argv, suite=suite(),
... plugins=[BetterConfiguringPlugin()]) # doctest: +REPORT_NDIFF
Widgets can frobnicate (or not) ... ok
Widgets might like cheese ... ok
<BLANKLINE>
----------------------------------------------------------------------
Ran 2 tests in ...s
<BLANKLINE>
OK
.. _Pylons: http://pylonshq.com/
.. _Django: http://www.djangoproject.com/
|