~ubuntu-branches/ubuntu/trusty/nose2/trusty

« back to all changes in this revision

Viewing changes to docs/dev/writing_plugins.rst

  • Committer: Package Import Robot
  • Author(s): Barry Warsaw
  • Date: 2013-09-09 22:14:45 UTC
  • Revision ID: package-import@ubuntu.com-20130909221445-zdvvvebxfucvavw5
Tags: upstream-0.4.7
ImportĀ upstreamĀ versionĀ 0.4.7

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
===============
 
2
Writing Plugins
 
3
===============
 
4
 
 
5
nose2 supports plugins for test collection, selection, observation and
 
6
reporting -- among other things. There are two basic rules for plugins:
 
7
 
 
8
* Plugin classes must subclass :class:`nose2.events.Plugin`.
 
9
 
 
10
* Plugins may implement any of the methods described in the
 
11
  :doc:`hook_reference`.
 
12
 
 
13
 
 
14
Hello World
 
15
===========
 
16
 
 
17
Here's a basic plugin. It doesn't do anything besides log a message at
 
18
the start of a test run.
 
19
 
 
20
.. code-block:: python
 
21
 
 
22
    import logging
 
23
    import os
 
24
 
 
25
    from nose2.events import Plugin
 
26
 
 
27
    log = logging.getLogger('nose2.plugins.helloworld')
 
28
 
 
29
    class HelloWorld(Plugin):
 
30
        configSection = 'helloworld'
 
31
        commandLineSwitch = (None, 'hello-world', 'Say hello!')
 
32
 
 
33
        def startTestRun(self, event):
 
34
            log.info('Hello pluginized world!')
 
35
 
 
36
To see this plugin in action, save it into an importable module, then
 
37
add that module to the ``plugins`` key in the ``[unittest]`` section
 
38
of a config file loaded by nose2, such as ``unittest.cfg``. Then run
 
39
nose2::
 
40
 
 
41
  nose2 --log-level=INFO --hello-world
 
42
 
 
43
And you should see the log message before the first dot appears.
 
44
 
 
45
 
 
46
Loading plugins
 
47
===============
 
48
 
 
49
As mentioned above, for nose2 to find a plugin, it must be in an
 
50
importable module, and the module must be listed under the ``plugins``
 
51
key in the ``[unittest]`` section of a config file loaded by nose2:
 
52
 
 
53
.. code-block:: ini
 
54
 
 
55
   [unittest]
 
56
   plugins = mypackage.someplugin
 
57
             otherpackage.thatplugin
 
58
             thirdpackage.plugins.metoo
 
59
 
 
60
As you can see, plugin *modules* are listed, one per line. All plugin
 
61
classes in those modules will be loaded -- but not necessarily
 
62
active. Typically plugins do not activate themselves ("register")
 
63
without seeing a command-line flag, or ``always-on = True`` in their
 
64
config file section.
 
65
 
 
66
 
 
67
Command-line Options
 
68
====================
 
69
 
 
70
nose2 uses `argparse`_ for command-line argument parsing. Plugins may
 
71
enable command-line options that register them as active, or take
 
72
arguments or flags controlling their operation.
 
73
 
 
74
The most basic thing to do is to set the plugin's
 
75
``commandLineSwitch`` attribute, which will automatically add a
 
76
command-line flag that registers the plugin.
 
77
 
 
78
To add other flags or arguments, you can use the Plugin methods
 
79
:meth:`nose2.events.Plugin.addFlag`,
 
80
:meth:`nose2.events.Plugin.addArgument` or
 
81
:meth:`nose2.events.Plugin.addOption`. If those don't offer enough
 
82
flexibility, you can directly manipulate the argument parser by
 
83
accessing ``self.session.argparse`` or the plugin option group by
 
84
accessing ``self.session.pluginargs``.
 
85
 
 
86
Please note though that the *majority* of your plugin's configuration
 
87
should be done via config file options, not command line options.
 
88
 
 
89
 
 
90
Config File Options
 
91
===================
 
92
 
 
93
Plugins may specify a config file section that holds their
 
94
configuration by setting their ``configSection`` attribute. All
 
95
plugins, regardless of whether they specify a config section, have a
 
96
``config`` attribute that holds a :class:`nose2.config.Config`
 
97
instance. This will be empty of values if the plugin does not specify
 
98
a config section or if no loaded config file includes that section.
 
99
 
 
100
Plugins should extract the user's configuration selections from their
 
101
config attribute in their ``__init__`` methods. Plugins that want to
 
102
use nose2's `Sphinx`_ extension to automatically document themselves
 
103
**must** do so.
 
104
 
 
105
Config file options may be extracted as strings, ints, booleans or
 
106
lists.
 
107
 
 
108
You should provide reasonable defaults for all config options.
 
109
 
 
110
Guidelines
 
111
==========
 
112
 
 
113
Events
 
114
------
 
115
 
 
116
nose2's plugin api is based on the api in unittest2's
 
117
under-development plugins branch. It differs from nose's plugins api
 
118
in one major area: what it passes to hooks. Where nose passes a
 
119
variety of arguments, nose2 *always passes an event*. The events are
 
120
listed in the :doc:`event_reference`.
 
121
 
 
122
Here's the key thing about that: *event attributes are
 
123
read-write*. Unless stated otherwise in the documentation for a hook,
 
124
you can set a new value for any event attribute, and *this will do
 
125
something*. Plugins and nose2 systems will see that new value and
 
126
either use it instead of what was originally set in the event
 
127
(example: the reporting stream or test executor), or use it to
 
128
supplement something they find elsewhere (example: extraTests on a
 
129
test loading event).
 
130
 
 
131
"Handling" events
 
132
~~~~~~~~~~~~~~~~~
 
133
 
 
134
Many hooks give plugins a chance to completely handle events,
 
135
bypassing other plugins and any core nose2 operations. To do this, a
 
136
plugin sets ``event.handled`` to True and, generally, returns an
 
137
appropriate value from the hook method. What is an appropriate value
 
138
varies by hook, and some hooks *can't* be handled in this way. But
 
139
even for hooks where handling the event doesn't stop all processing,
 
140
it *will* stop subsequently-loaded plugins from seeing the event.
 
141
 
 
142
Logging
 
143
-------
 
144
 
 
145
nose2 uses the logging classes from the standard library. To enable users
 
146
to view debug messages easily, plugins should use ``logging.getLogger()`` to
 
147
acquire a logger in the ``nose2.plugins`` namespace.
 
148
 
 
149
.. todo ::
 
150
 
 
151
   more guidelines
 
152
 
 
153
Recipes
 
154
=======
 
155
 
 
156
* Writing a plugin that monitors or controls test result output
 
157
 
 
158
  Implement any of the ``report*`` hook methods, especially if you
 
159
  want to output to the console. If outputing to file or other system,
 
160
  you might implement :func:`testOutcome` instead.
 
161
 
 
162
  Example: :class:`nose2.plugins.result.ResultReporter`
 
163
 
 
164
* Writing a plugin that handles exceptions
 
165
 
 
166
  If you just want to handle some exceptions as skips or failures
 
167
  instead of errors, see :class:`nose2.plugins.outcomes.Outcomes`,
 
168
  which offers a simple way to do that. Otherwise, implement
 
169
  :func:`setTestOutcome` to change test outcomes.
 
170
 
 
171
  Example: :class:`nose2.plugins.outcomes.Outcomes`
 
172
 
 
173
* Writing a plugin that adds detail to error reports
 
174
 
 
175
  Implement :func:`testOutcome` and put your extra information into
 
176
  ``event.metadata``, then implement :func:`outcomeDetail` to extract
 
177
  it and add it to the error report.
 
178
 
 
179
  Examples: :class:`nose2.plugins.buffer.OutputBufferPlugin`, :class:`nose2.plugins.logcapture.LogCapture`
 
180
 
 
181
* Writing a plugin that loads tests from files other than python modules
 
182
 
 
183
  Implement :func:`handleFile`.
 
184
 
 
185
  Example: :class:`nose2.plugins.doctests.DocTestLoader`
 
186
 
 
187
* Writing a plugin that loads tests from python modules
 
188
 
 
189
  Implement at least :func:`loadTestsFromModule`.
 
190
 
 
191
  .. _loading-from-module:
 
192
 
 
193
  .. warning ::
 
194
 
 
195
     One thing to beware of here is that if you return tests as
 
196
     dynamically-generated test cases, or instances of a testcase
 
197
     class that is defined *anywhere* but the module being loaded, you
 
198
     *must* use :func:`nose2.util.transplant_class` to make the test
 
199
     case class appear to have originated in that module. Otherwise,
 
200
     module-level fixtures will not work for that test, and may be
 
201
     ignored entirely for the module if there are no test cases that
 
202
     are or appear to be defined there.
 
203
 
 
204
* Writing a plugin that prints a report
 
205
 
 
206
  Implement :func:`beforeErrorList`, :func:`beforeSummaryReport` or
 
207
  :func:`afterSummaryReport`
 
208
 
 
209
  Example: :class:`nose2.plugins.prof.Profiler`
 
210
 
 
211
* Writing a plugin that selects or rejects tests
 
212
 
 
213
  Implement :class:`matchPath` or :class:`getTestCaseNames`.
 
214
 
 
215
  Example: :class:`nose2.plugins.loader.parameters.Parameters`
 
216
 
 
217
.. _argparse : http://pypi.python.org/pypi/argparse/1.2.1
 
218
.. _Sphinx : http://sphinx.pocoo.org/