largely improve and reshuffle docs, heading strongly towards a 1.1.0

--HG--
branch : trunk
This commit is contained in:
holger krekel 2009-11-05 03:18:55 +01:00
parent b04a04cabd
commit a5a94c4e8f
31 changed files with 645 additions and 780 deletions

View File

@ -3,28 +3,29 @@ import os, sys
WIDTH = 75
plugins = [
('plugins for Python test functions',
'skipping figleaf monkeypatch capture recwarn',),
('plugins for other testing styles and languages',
'oejskit unittest nose django doctest restdoc'),
('plugins for generic reporting and failure logging',
'pastebin resultlog terminal',),
('plugins for generic reporting and failure logging',
'pastebin resultlog terminal',),
('misc plugins / core functionality',
'helpconfig pdb mark hooklog')
('advanced python testing',
'skipping mark pdb figleaf coverage '
'monkeypatch capture recwarn tmpdir',),
('testing domains',
'oejskit django'),
('reporting and failure logging',
'pastebin xmlresult resultlog terminal',),
('other testing conventions',
'unittest nose doctest restdoc'),
('core debugging / help functionality',
'helpconfig hooklog')
#('internal plugins / core functionality',
# #'pdb keyword hooklog runner execnetcleanup # pytester',
# 'pdb keyword hooklog runner execnetcleanup' # pytester',
# #'runner execnetcleanup # pytester',
# 'runner execnetcleanup' # pytester',
#)
]
externals = {
'oejskit': "run javascript tests in real life browsers",
'django': "support for testing django applications",
# 'coverage': "support for using Ned's coverage module",
# 'xmlresult': "support for generating xml reports "
# "and CruiseControl integration",
'oejskit': "run javascript tests in real life browsers",
'django': "for testing django applications",
'coverage': "for testing with Ned's coverage module ",
'xmlresult': "for generating xml reports "
"and CruiseControl integration",
}
def warn(*args):
@ -136,7 +137,7 @@ class PluginOverview(RestWriter):
docpath = self.target.dirpath(name).new(ext=".txt")
if oneliner is not None:
htmlpath = docpath.new(ext='.html')
self.para("%s_ %s" %(name, oneliner))
self.para("%s_ (3rd) %s" %(name, oneliner))
self.add_internal_link(name, htmlpath)
else:
doc = PluginDoc(docpath)
@ -212,7 +213,7 @@ class PluginDoc(RestWriter):
# "py/test/plugin/%s" %(hg_changeset, basename)))
self.links.append((basename,
"http://bitbucket.org/hpk42/py-trunk/raw/%s/"
"_py/test/plugin/%s" %(pyversion, basename)))
"py/plugin/%s" %(pyversion, basename)))
self.links.append(('customize', '../customize.html'))
self.links.append(('plugins', 'index.html'))
self.links.append(('get in contact', '../../contact.html'))

View File

@ -1,329 +0,0 @@
"""
Tested with coverage 2.85 and pygments 1.0
TODO:
+ 'html-output/*,cover' should be deleted
+ credits for coverage
+ credits for pygments
+ 'Install pygments' after ImportError is to less
+ is the way of determining DIR_CSS_RESOURCE ok?
+ write plugin test
+ '.coverage' still exists in py.test execution dir
"""
import os
import sys
import re
import shutil
from StringIO import StringIO
import py
try:
from pygments import highlight
from pygments.lexers import get_lexer_by_name
from pygments.formatters import HtmlFormatter
except ImportError:
print "Install pygments" # XXX
sys.exit(0)
DIR_CUR = str(py.path.local())
REPORT_FILE = os.path.join(DIR_CUR, '.coverage')
DIR_ANNOTATE_OUTPUT = os.path.join(DIR_CUR, '.coverage_annotate')
COVERAGE_MODULES = set()
# coverage output parsing
REG_COVERAGE_SUMMARY = re.compile('([a-z_\.]+) +([0-9]+) +([0-9]+) +([0-9]+%)')
REG_COVERAGE_SUMMARY_TOTAL = re.compile('(TOTAL) +([0-9]+) +([0-9]+) +([0-9]+%)')
DEFAULT_COVERAGE_OUTPUT = '.coverage_annotation'
# HTML output specific
DIR_CSS_RESOURCE = os.path.dirname(__import__('pytest_coverage').__file__)
CSS_RESOURCE_FILES = ['header_bg.jpg', 'links.gif']
COVERAGE_TERM_HEADER = "\nCOVERAGE INFORMATION\n" \
"====================\n"
HTML_INDEX_HEADER = '''<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>py.test - Coverage Index</title>
<style type="text/css">
table {
font-size:0.9em;
font-family: Arial, Helvetica, verdana sans-serif;
background-color:#fff;
border-collapse: collapse;
width: 500px;
}
caption {
font-size: 25px;
color: #1ba6b2;
font-weight: bold;
text-align: left;
background: url(header_bg.jpg) no-repeat top left;
padding: 10px;
margin-bottom: 2px;
}
thead th {
border-right: 1px solid #fff;
color:#fff;
text-align:center;
padding:2px;
text-transform:uppercase;
height:25px;
background-color: #a3c159;
font-weight: normal;
}
tfoot {
color:#1ba6b2;
padding:2px;
text-transform:uppercase;
font-size:1.2em;
font-weigth: bold;
margin-top:6px;
border-top: 6px solid #e9f7f6;
}
tfoot td {
text-align: left;
}
tbody tr {
background-color:#fff;
border-bottom: 1px solid #f0f0f0;
}
tbody td {
color:#414141;
padding:5px;
text-align:left;
}
tbody th {
text-align:left;
padding:2px;
}
tbody td a, tbody th a {
color:#6C8C37;
text-decoration:none;
font-weight:normal;
display:block;
background: transparent url(links.gif) no-repeat 0% 50%;
padding-left:15px;
}
tbody td a:hover, tbody th a:hover {
color:#009193;
text-decoration:none;
}
</style>
</head>
<body
<table >
<caption>Module Coverage</caption>
<tbody>
<thead>
<tr>
<th>Module</th>
<th>Statements</th>
<th>Executed</th>
<th>Coverage</th>
</tr>
</thead>'''
HTML_INDEX_FOOTER = ''' </tbody>
</table>
</body>
</html>'''
class CoverageHtmlFormatter(HtmlFormatter):
"""XXX: doc"""
def __init__(self, *args, **kwargs):
HtmlFormatter.__init__(self,*args, **kwargs)
self.annotation_infos = kwargs.get('annotation_infos')
def _highlight_lines(self, tokensource):
"""
XXX: doc
"""
hls = self.hl_lines
self.annotation_infos = [None] + self.annotation_infos
hls = [l for l, i in enumerate(self.annotation_infos) if i]
for i, (t, value) in enumerate(tokensource):
if t != 1:
yield t, value
if i + 1 in hls: # i + 1 because Python indexes start at 0
if self.annotation_infos[i+1] == "!":
yield 1, '<span style="background-color:#FFE5E5">%s</span>' \
% value
elif self.annotation_infos[i+1] == ">":
yield 1, '<span style="background-color:#CCFFEB">%s</span>' \
% value
else:
raise ValueError("HHAHA: %s" % self.annotation_infos[i+1])
else:
yield 1, value
def _rename_annotation_files(module_list, dir_annotate_output):
for m in module_list:
mod_fpath = os.path.basename(m.__file__)
if mod_fpath.endswith('pyc'):
mod_fpath = mod_fpath[:-1]
old = os.path.join(dir_annotate_output, '%s,cover'% mod_fpath)
new = os.path.join(dir_annotate_output, '%s,cover'% m.__name__)
if os.path.isfile(old):
shutil.move(old, new)
yield new
def _generate_module_coverage(mc_path, anotation_infos, src_lines):
#XXX: doc
code = "".join(src_lines)
mc_path = "%s.html" % mc_path
lexer = get_lexer_by_name("python", stripall=True)
formatter = CoverageHtmlFormatter(linenos=True, noclasses=True,
hl_lines=[1], annotation_infos=anotation_infos)
result = highlight(code, lexer, formatter)
fp = open(mc_path, 'w')
fp.write(result)
fp.close()
def _parse_modulecoverage(mc_fpath):
#XXX: doc
fd = open(mc_fpath, 'r')
anotate_infos = []
src_lines = []
for line in fd.readlines():
anotate_info = line[0:2].strip()
if not anotate_info:
anotate_info = None
src_line = line[2:]
anotate_infos.append(anotate_info)
src_lines.append(src_line)
return mc_fpath, anotate_infos, src_lines
def _parse_coverage_summary(fd):
"""Parses coverage summary output."""
if hasattr(fd, 'readlines'):
fd.seek(0)
for l in fd.readlines():
m = REG_COVERAGE_SUMMARY.match(l)
if m:
# yield name, stmts, execs, cover
yield m.group(1), m.group(2), m.group(3), m.group(4)
else:
m = REG_COVERAGE_SUMMARY_TOTAL.match(l)
if m:
# yield name, stmts, execs, cover
yield m.group(1), m.group(2), m.group(3), m.group(4)
def _get_coverage_index(mod_name, stmts, execs, cover, annotation_dir):
"""
Generates the index page where are all modulare coverage reports are
linked.
"""
if mod_name == 'TOTAL':
return '<tfoot><tr style="text-align: center;font-weigth:bold;font-size:1.2em; "><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr></tfoot>\n' % (mod_name, stmts, execs, cover)
covrep_fpath = os.path.join(annotation_dir, '%s,cover.html' % mod_name)
assert os.path.isfile(covrep_fpath) == True
fname = os.path.basename(covrep_fpath)
modlink = '<a href="%s">%s</a>' % (fname, mod_name)
return '<tr style="text-align: center;"><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>\n' % (modlink, stmts, execs, cover)
class CoveragePlugin:
def pytest_addoption(self, parser):
group = parser.addgroup('coverage options')
group.addoption('-C', action='store_true', default=False,
dest = 'coverage',
help=('displays coverage information.'))
group.addoption('--coverage-html', action='store', default=False,
dest='coverage_annotation',
help='path to the coverage HTML output dir.')
group.addoption('--coverage-css-resourcesdir', action='store',
default=DIR_CSS_RESOURCE,
dest='coverage_css_ressourcedir',
help='path to dir with css-resources (%s) for '
'being copied to the HTML output dir.' % \
", ".join(CSS_RESOURCE_FILES))
def pytest_configure(self, config):
if config.getvalue('coverage'):
try:
import coverage
except ImportError:
raise config.Error("To run use the coverage option you have to install " \
"Ned Batchelder's coverage: "\
"http://nedbatchelder.com/code/modules/coverage.html")
self.coverage = coverage
self.summary = None
def pytest_terminal_summary(self, terminalreporter):
if hasattr(self, 'coverage'):
self.coverage.stop()
module_list = [sys.modules[mod] for mod in COVERAGE_MODULES]
module_list.sort()
summary_fd = StringIO()
# get coverage reports by module list
self.coverage.report(module_list, file=summary_fd)
summary = COVERAGE_TERM_HEADER + summary_fd.getvalue()
terminalreporter._tw.write(summary)
config = terminalreporter.config
dir_annotate_output = config.getvalue('coverage_annotation')
if dir_annotate_output:
if dir_annotate_output == "":
dir_annotate_output = DIR_ANNOTATE_OUTPUT
# create dir
if os.path.isdir(dir_annotate_output):
shutil.rmtree(dir_annotate_output)
os.mkdir(dir_annotate_output)
# generate annotation text files for later parsing
self.coverage.annotate(module_list, dir_annotate_output)
# generate the separate module coverage reports
for mc_fpath in _rename_annotation_files(module_list, \
dir_annotate_output):
# mc_fpath, anotate_infos, src_lines from _parse_do
_generate_module_coverage(*_parse_modulecoverage(mc_fpath))
# creating contents for the index pagee for coverage report
idxpage_html = StringIO()
idxpage_html.write(HTML_INDEX_HEADER)
total_sum = None
for args in _parse_coverage_summary(summary_fd):
# mod_name, stmts, execs, cover = args
idxpage_html.write(_get_coverage_index(*args, \
**dict(annotation_dir=dir_annotate_output)))
idxpage_html.write(HTML_INDEX_FOOTER)
idx_fpath = os.path.join(dir_annotate_output, 'index.html')
idx_fd = open(idx_fpath, 'w')
idx_fd.write(idxpage_html.getvalue())
idx_fd.close()
dir_css_resource_dir = config.getvalue('coverage_css_ressourcedir')
if dir_annotate_output and dir_css_resource_dir != "":
if not os.path.isdir(dir_css_resource_dir):
raise config.Error("CSS resource dir not found: '%s'" % \
dir_css_resource_dir)
for r in CSS_RESOURCE_FILES:
src = os.path.join(dir_css_resource_dir, r)
if os.path.isfile(src):
dest = os.path.join(dir_annotate_output, r)
shutil.copy(src, dest)
def pytest_collectstart(self, collector):
if isinstance(collector, py.__.test.pycollect.Module):
COVERAGE_MODULES.update(getattr(collector.obj,
'COVERAGE_MODULES', []))
def pytest_testrunstart(self):
print "self.coverage", self.coverage
if hasattr(self, 'coverage'):
print "START coverage"
self.coverage.erase()
self.coverage.start()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 B

View File

@ -0,0 +1,115 @@
py.test/pylib 1.1.0: Python3, Jython, advanced skipping, cleanups ...
--------------------------------------------------------------------------------
Features:
* compatible to Python3 (single py2/py3 source), works with Distribute
* generalized marking_: mark tests one a whole-class or whole-module basis
* conditional skipping_: skip/xfail based on platform/dependencies
Fixes:
* code reduction and "de-magification" (e.g. 23 KLoc -> 11 KLOC)
* distribute testing requires the now separately released 'execnet' package
* funcarg-setup/caching, "same-name" test modules now cause an exlicit error
* de-cluttered reporting, --report option for skipped/xfail details
Compatibilities
1.1.0 should allow running test code that already worked well with 1.0.2
plus some more due to improved unittest/nose compatibility.
More information:
http://pytest.org
thanks and have fun,
holger (http://twitter.com/hpk42)
.. _marking: ../test/plugin/mark.html
.. _skipping: ../test/plugin/skipping.html
Changelog 1.0.2 -> 1.1.0
-----------------------------------------------------------------------
* remove py.rest tool and internal namespace - it was
never really advertised and can still be used with
the old release if needed. If there is interest
it could be revived into its own tool i guess.
* fix issue48 and issue59: raise an Error if the module
from an imported test file does not seem to come from
the filepath - avoids "same-name" confusion that has
been reported repeatedly
* merged Ronny's nose-compatibility hacks: now
nose-style setup_module() and setup() functions are
supported
* introduce generalized py.test.mark function marking
* reshuffle / refine command line grouping
* deprecate parser.addgroup in favour of getgroup which creates option group
* add --report command line option that allows to control showing of skipped/xfailed sections
* generalized skipping: a new way to mark python functions with skipif or xfail
at function, class and modules level based on platform or sys-module attributes.
* extend py.test.mark decorator to allow for positional args
* introduce and test "py.cleanup -d" to remove empty directories
* fix issue #59 - robustify unittest test collection
* make bpython/help interaction work by adding an __all__ attribute
to ApiModule, cleanup initpkg
* use MIT license for pylib, add some contributors
* remove py.execnet code and substitute all usages with 'execnet' proper
* fix issue50 - cached_setup now caches more to expectations
for test functions with multiple arguments.
* merge Jarko's fixes, issue #45 and #46
* add the ability to specify a path for py.lookup to search in
* fix a funcarg cached_setup bug probably only occuring
in distributed testing and "module" scope with teardown.
* many fixes and changes for making the code base python3 compatible,
many thanks to Benjamin Peterson for helping with this.
* consolidate builtins implementation to be compatible with >=2.3,
add helpers to ease keeping 2 and 3k compatible code
* deprecate py.compat.doctest|subprocess|textwrap|optparse
* deprecate py.magic.autopath, remove py/magic directory
* move pytest assertion handling to py/code and a pytest_assertion
plugin, add "--no-assert" option, deprecate py.magic namespaces
in favour of (less) py.code ones.
* consolidate and cleanup py/code classes and files
* cleanup py/misc, move tests to bin-for-dist
* introduce delattr/delitem/delenv methods to py.test's monkeypatch funcarg
* consolidate py.log implementation, remove old approach.
* introduce py.io.TextIO and py.io.BytesIO for distinguishing between
text/unicode and byte-streams (uses underlying standard lib io.*
if available)
* make py.unittest_convert helper script available which converts "unittest.py"
style files into the simpler assert/direct-test-classes py.test/nosetests
style. The script was written by Laura Creighton.
* simplified internal localpath implementation

View File

@ -5,10 +5,12 @@ Release notes
Contents:
.. toctree::
:maxdepth: 1
:maxdepth: 2
announce/release-1.0.2
announce/release-1.0.1
announce/release-1.0.0
announce/release-0.9.2
announce/release-0.9.0
.. include: release-1.1.0
.. include: release-1.0.2
release-1.0.1
release-1.0.0
release-0.9.2
release-0.9.0

View File

@ -1,6 +1,8 @@
Changes between 1.0.2 and '1.1.0b1'
Changes between 1.1.0 and 1.0.2
=====================================
* adjust and improve docs
* remove py.rest tool and internal namespace - it was
never really advertised and can still be used with
the old release if needed. If there is interest
@ -49,6 +51,9 @@ Changes between 1.0.2 and '1.1.0b1'
* fix a funcarg cached_setup bug probably only occuring
in distributed testing and "module" scope with teardown.
* many fixes and changes for making the code base python3 compatible,
many thanks to Benjamin Peterson for helping with this.
* consolidate builtins implementation to be compatible with >=2.3,
add helpers to ease keeping 2 and 3k compatible code

View File

@ -18,7 +18,7 @@ Contents of the library
Every object in the ``py.code`` library wraps a code Python object related
to code objects, source code, frames and tracebacks: the ``py.code.Code``
class wraps code objects, ``py.code.Source`` source snippets,
``py.code.Traceback` exception tracebacks, :api:`py.code.Frame`` frame
``py.code.Traceback` exception tracebacks, ``py.code.Frame`` frame
objects (as found in e.g. tracebacks) and ``py.code.ExceptionInfo`` the
tuple provided by sys.exc_info() (containing exception and traceback
information when an exception occurs). Also in the library is a helper function

View File

@ -1,6 +1,6 @@
import py
from _py.test.plugin.pytest_restdoc import convert_rest_html, strip_html_header
from py.plugin.pytest_restdoc import convert_rest_html, strip_html_header
html = py.xml.html
@ -57,23 +57,23 @@ pageTracker._trackPageview();
def fill_menubar(self):
items = [
self.a_docref("install", "install.html"),
self.a_docref("contact", "contact.html"),
self.a_docref("changelog", "changelog.html"),
self.a_docref("faq", "faq.html"),
self.a_docref("INSTALL", "install.html"),
self.a_docref("CONTACT", "contact.html"),
self.a_docref("CHANGELOG", "changelog.html"),
self.a_docref("FAQ", "faq.html"),
html.div(
html.h3("py.test:"),
self.a_docref("doc index", "test/index.html"),
self.a_docref("features", "test/features.html"),
self.a_docref("quickstart", "test/quickstart.html"),
self.a_docref("tutorials", "test/talks.html"),
self.a_docref("plugins", "test/plugin/index.html"),
self.a_docref("funcargs", "test/funcargs.html"),
self.a_docref("customize", "test/customize.html"),
self.a_docref("Index", "test/index.html"),
self.a_docref("Quickstart", "test/quickstart.html"),
self.a_docref("Features", "test/features.html"),
self.a_docref("Plugins", "test/plugin/index.html"),
self.a_docref("Funcargs", "test/funcargs.html"),
self.a_docref("Customize", "test/customize.html"),
self.a_docref("Tutorials", "test/talks.html"),
),
html.div(
html.h3("supporting APIs:"),
self.a_docref("pylib index", "index.html"),
self.a_docref("Index", "index.html"),
self.a_docref("py.path", "path.html"),
self.a_docref("py.code", "code.html"),
)
@ -85,9 +85,10 @@ pageTracker._trackPageview();
self.menubar = html.div(id=css.menubar, *[
html.div(item) for item in items])
version = py.version
announcelink = self.a_docref("%s ANN" % version,
"announce/release-%s.html" %(version,))
self.menubar.insert(0,
html.div("%s" % (py.version), style="font-style: italic;")
)
html.div(announcelink))
#self.a_href("%s-%s" % (self.title, py.version),
# "http://pypi.python.org/pypi/py/%s" % version,
#id="versioninfo",

View File

@ -13,75 +13,79 @@ On naming, nosetests, licensing and magic
Why the ``py`` naming? what is it?
------------------------------------
Because the name was kind of available and there was the
Because the name was available and there was the
idea to have the package evolve into a "standard" library
kind of thing that works cross-python versions and is
not tied to a particular CPython revision or its release
cycle. Clearly, this was ambitious and the naming
has maybe haunted the project rather than helping it.
There may be a project name change and possibly a
split up into different projects sometime.
Why the ``py.test`` naming?
------------------------------------
the py lib contains other command line tools that
all share the ``py.`` prefix which makes it easy
to use TAB-completion on the shell. Another motivation
was to make it obvious where testing functionality
for the ``py.test`` command line tool is: in the
``py.test`` package name space.
because of TAB-completion under Bash/Shells. If you hit
``py.<TAB>`` you'll get a list of available development
tools that all share the ``py.`` prefix. Another motivation
was to unify the package ("py.test") and tool filename.
What's py.test's relation to ``nosetests``?
---------------------------------------------
py.test and nose_ share basic philosophy when it comes
to running Python tests. In fact,
with py.test-1.0.1 it is easy to run many test suites
with py.test-1.1.0 it is ever easier to run many test suites
that currently work with ``nosetests``. nose_ was created
as a clone of ``py.test`` when it was in the ``0.8`` release
as a clone of ``py.test`` when py.test was in the ``0.8`` release
cycle so some of the newer features_ introduced with py.test-1.0
have no counterpart in nose_.
and py.test-1.1 have no counterpart in nose_.
.. _nose: http://somethingaboutorange.com/mrl/projects/nose/0.11.1/
.. _features: test/features.html
.. _apipkg: http://pypi.python.org/pypi/apipkg
What's all this "magic" with py.test?
What's this "magic" with py.test?
----------------------------------------
"All this magic" usually boils down to two issues:
issues where people have used the term "magic" in the past:
* There is a special tweak to importing: `py/__init__.py`_ contains
a dictionary which maps the importable ``py.*`` namespaces to
objects in files. When looking at the project source code
you see imports like ``from py.__.test.session import Session``. The
the double ``__`` underscore indicates the "normal" python
filesystem/namespace coupled import, i.e. it points to
``py/test/session.py``'s ``Session`` object. However,
from the outside you use the "non-underscore" `py namespaces`_
so this distinction usually only shows up if you hack
on internal code or see internal tracebacks.
* `py/__init__.py`_ uses the apipkg_ mechanism for lazy-importing
and full control on what API you get when importing "import py".
* when an ``assert`` fails, py.test re-interprets the expression
to show intermediate values. This allows to use the plain ``assert``
statement instead of the many methods that you otherwise need
to mimick this behaviour. This means that in case of a failing
assert, your expressions gets evaluated *twice*. If your expression
has side effects the outcome may be different. If the test suddenly
passes you will get a detailed message. It is good practise, anyway,
to not have asserts with side effects. ``py.test --nomagic`` turns
off assert re-intepretation.
* when an ``assert`` statement fails, py.test re-interprets the expression
to show intermediate values if a test fails. If your expression
has side effects the intermediate values may not be the same, obfuscating
the initial error (this is also explained at the command line if it happens).
``py.test --no-assert`` turns off assert re-intepretation.
Sidenote: it is good practise to avoid asserts with side effects.
Other than that, ``py.test`` has bugs or quirks like any other computer
software. In fact, it has a *strong* focus on running robustly and has
over a thousand automated tests for its own code base.
.. _`py namespaces`: index.html
.. _`py/__init__.py`: http://bitbucket.org/hpk42/py-trunk/src/1.0.x/py/__init__.py
.. _`py/__init__.py`: http://bitbucket.org/hpk42/py-trunk/src/trunk/py/__init__.py
function arguments and parametrized tests
===============================================
function arguments, parametrized tests and setup
====================================================
.. _funcargs: test/funcargs.html
Is using funcarg- versus xUnit-based setup a style question?
---------------------------------------------------------------
It depends. For simple applications or for people experienced
with nose_ or unittest-style test setup using `xUnit style setup`_
make some sense. For larger test suites, parametrized testing
or setup of complex test resources using funcargs_ is recommended.
Moreover, funcargs are ideal for writing advanced test support
code (like e.g. the monkeypatch_, the tmpdir_ or capture_ funcargs)
because the support code can register setup/teardown functions
in a managed class/module/function scope.
.. _monkeypatch: test/plugin/monkeypatch.html
.. _tmpdir: test/plugin/tmpdir.html
.. _capture: test/plugin/capture.html
.. _`xUnit style setup`: test/xunit_setup.html
.. _`pytest_nose`: test/plugin/nose.html
.. _`why pytest_pyfuncarg__ methods?`:
@ -94,7 +98,7 @@ flexibility we decided to go for `Convention over Configuration`_ and
allow to directly specify the factory. Besides removing the need
for an indirection it allows to "grep" for ``pytest_funcarg__MYARG``
and will safely find all factory functions for the ``MYARG`` function
argument. It helps to alleviates the de-coupling of function
argument. It helps to alleviate the de-coupling of function
argument usage and creation.
.. _`Convention over Configuration`: http://en.wikipedia.org/wiki/Convention_over_Configuration

View File

@ -29,7 +29,6 @@ Other (minor) support functionality
For the latest Release, see `PyPI project page`_
.. _`download and installation`: download.html
.. _`py-dev at codespeak net`: http://codespeak.net/mailman/listinfo/py-dev
.. _`py.log`: log.html
.. _`py.io`: io.html

View File

@ -25,7 +25,7 @@ on Windows you might need to write down the full path to ``easy_install``.
The py lib and its tools are expected to work well on Linux,
Windows and OSX, Python versions 2.4, 2.5, 2.6 through to
the Python3 versions 3.0 and 3.1. Jython
the Python3 versions 3.0 and 3.1 and Jython
.. _mercurial: http://mercurial.selenic.com/wiki/
.. _`Distribute`:
@ -43,15 +43,13 @@ and documentation source with mercurial_::
hg clone https://bitbucket.org/hpk42/py-trunk/
This currrently contains a 1.0.x branch and the
default 'trunk' branch where mainline development
takes place.
Development usually takes place on the 'trunk' branch.
.. There also is a readonly subversion
checkout available which contains the latest release::
svn co https://codespeak.net/svn/py/dist
You can go to the python package index and
You can also go to the python package index and
download and unpack a TAR file::
http://pypi.python.org/pypi/py/
@ -64,7 +62,7 @@ With a working `Distribute`_ or setuptools_ installation you can type::
python setup.py develop
in order to work with the tools and the lib of your checkout.
in order to work inline with the tools and the lib of your checkout.
.. _`no-setuptools`:

View File

@ -40,7 +40,7 @@ a ``py.path.local`` object for us (which wraps a directory):
>>> foofile.read(1)
'b'
``py.path.svnurl` and :api:`py.path.svnwc``
``py.path.svnurl` and ``py.path.svnwc``
----------------------------------------------
Two other ``py.path`` implementations that the py lib provides wrap the

View File

@ -14,6 +14,8 @@ specify different Python versions and interpreters.
**Requirements**: you need to install the `execnet`_ package
to perform distributed test runs.
**NOTE**: Version 1.1.0 is not able to distribute tests across Python3/Python2 barriers.
Speed up test runs by sending tests to multiple CPUs
----------------------------------------------------------

View File

@ -26,7 +26,7 @@ naming patterns. As ``py.test`` operates as a separate
cmdline tool you can easily have a command line utility and
some tests in the same file.
supports many testing practises and methods
supports several testing practises and methods
==================================================================
py.test supports many testing methods conventionally used in

View File

@ -1,43 +1,301 @@
==========================================================
**funcargs**: test function arguments FTW
==========================================================
==============================================================
**funcargs**: advanced test setup and parametrization
==============================================================
.. contents::
:local:
:depth: 2
Goals of the "funcarg" mechanism
==========================================
what are "funcargs" and what are they good for?
=================================================
Since version 1.0 py.test features the "funcarg" mechanism which
allows a Python test function to take arguments independently provided
by factory functions. Factory functions allow to encapsulate
all setup and fixture glue code into nicely separated objects
and provide a natural way for writing python test functions.
Compared to `xUnit style`_ the new mechanism is meant to:
* make test functions easier to write and to read
* isolate test fixture creation to a single place
* bring new flexibility and power to test state management
* naturally extend towards parametrizing test functions
with multiple argument sets
* enable creation of zero-boilerplate test helper objects that
interact with the execution of a test function, see the
`blog post about the monkeypatch funcarg`_.
If you find issues or have further suggestions for improving
the mechanism you are welcome to checkout `contact possibilities`_ page.
Named parameters of a test function are called *funcargs* for short.
A Funcarg can be a simple number of a complex object. To perform a
test function call each parameter is setup by a factory function.
To call a test function repeatedly with different funcargs sets
test parameters can be generated.
.. _`contact possibilities`: ../contact.html
.. _`parametrizing tests, generalized`: http://tetamap.wordpress.com/2009/05/13/parametrizing-python-tests-generalized/
.. _`blog post about the monkeypatch funcarg`: http://tetamap.wordpress.com/2009/03/03/monkeypatching-in-unit-tests-done-right/
.. _`xUnit style`: xunit_setup.html
.. _`funcarg factory`:
.. _factory:
funcarg factories: setting up test function arguments
==============================================================
Test functions can specify one ore more arguments ("funcargs")
and a test module or plugin can define factory functions that provide
the function argument. Let's look at a simple self-contained
example that you can put into a test module:
.. sourcecode:: python
# ./test_simplefactory.py
def pytest_funcarg__myfuncarg(request):
return 42
def test_function(myfuncarg):
assert myfuncarg == 17
If you run this with ``py.test test_simplefactory.py`` you see something like this:
.. sourcecode:: python
=========================== test session starts ============================
python: platform linux2 -- Python 2.6.2
test object 1: /home/hpk/hg/py/trunk/example/funcarg/test_simplefactory.py
test_simplefactory.py F
================================ FAILURES ==================================
______________________________ test_function _______________________________
myfuncarg = 42
def test_function(myfuncarg):
> assert myfuncarg == 17
E assert 42 == 17
test_simplefactory.py:6: AssertionError
======================== 1 failed in 0.11 seconds ==========================
This means that the test function was called with a ``myfuncarg`` value
of ``42`` and the assert fails accordingly. Here is how py.test
calls the test function:
1. py.test discovers the ``test_function`` because of the ``test_`` prefix.
The test function needs a function argument named ``myfuncarg``.
A matching factory function is discovered by looking for the
name ``pytest_funcarg__myfuncarg``.
2. ``pytest_funcarg__myfuncarg(request)`` is called and
returns the value for ``myfuncarg``.
3. ``test_function(42)`` call is executed.
Note that if you misspell a function argument or want
to use one that isn't available, you'll see an error
with a list of available function arguments.
factory functions receive a `request object`_
which they can use to register setup/teardown
functions or access meta data about a test.
.. _`request object`:
funcarg factory request objects
------------------------------------------
Request objects are passed to funcarg factories and allow
to access test configuration, test context and `useful caching
and finalization helpers`_. Here is a list of attributes:
``request.function``: python function object requesting the argument
``request.cls``: class object where the test function is defined in or None.
``request.module``: module object where the test function is defined in.
``request.config``: access to command line opts and general config
``request.param``: if exists was passed by a previous `metafunc.addcall`_
.. _`useful caching and finalization helpers`:
registering funcarg related finalizers/cleanup
----------------------------------------------------
.. sourcecode:: python
def addfinalizer(func):
""" call a finalizer function when test function finishes. """
Calling ``request.addfinalizer()`` is useful for scheduling teardown
functions. Here is an example for providing a ``myfile``
object that is to be closed when the execution of a
test function finishes.
.. sourcecode:: python
def pytest_funcarg__myfile(self, request):
# ... create and open a unique per-function "myfile" object ...
request.addfinalizer(lambda: myfile.close())
return myfile
managing fixtures across test modules and test runs
----------------------------------------------------------
.. sourcecode:: python
def cached_setup(setup, teardown=None, scope="module", extrakey=None):
""" cache and return result of calling setup().
The requested argument name, the scope and the ``extrakey``
determine the cache key. The scope also determines when
teardown(result) will be called. valid scopes are:
scope == 'function': when the single test function run finishes.
scope == 'module': when tests in a different module are run
scope == 'session': when tests of the session have run.
"""
Calling ``request.cached_setup()`` helps you to manage fixture
objects across several scopes. For example, for creating a Database object
that is to be setup only once during a test session you can use the helper
like this:
.. sourcecode:: python
def pytest_funcarg__database(request):
return request.cached_setup(
setup=lambda: Database("..."),
teardown=lambda val: val.close(),
scope="session"
)
requesting values of other funcargs
---------------------------------------------
.. sourcecode:: python
def getfuncargvalue(name):
""" Lookup and call function argument factory for the given name.
Each function argument is only created once per function setup.
"""
``request.getfuncargvalue(name)`` calls another funcarg factory function.
You can use this function if you want to `decorate a funcarg`_, i.e.
you want to provide the "normal" value but add something
extra. If a factory cannot be found a ``request.Error``
exception will be raised.
.. _`test generators`:
.. _`parametrizing-tests`:
generating parametrized tests
===========================================================
You can parametrize multiple runs of the same test
function by adding new test function calls with different
function argument values. Let's look at a simple self-contained
example:
.. sourcecode:: python
# ./test_example.py
def pytest_generate_tests(metafunc):
if "numiter" in metafunc.funcargnames:
for i in range(10):
metafunc.addcall(funcargs=dict(numiter=i))
def test_func(numiter):
assert numiter < 9
If you run this with ``py.test test_example.py`` you'll get:
.. sourcecode:: python
============================= test session starts ==========================
python: platform linux2 -- Python 2.6.2
test object 1: /home/hpk/hg/py/trunk/test_example.py
test_example.py .........F
================================ FAILURES ==================================
__________________________ test_func.test_func[9] __________________________
numiter = 9
def test_func(numiter):
> assert numiter < 9
E assert 9 < 9
/home/hpk/hg/py/trunk/test_example.py:10: AssertionError
Here is what happens in detail:
1. ``pytest_generate_tests(metafunc)`` hook is called once for each test
function. It adds ten new function calls with explicit function arguments.
2. **execute tests**: ``test_func(numiter)`` is called ten times with
ten different arguments.
.. _`metafunc object`:
test generators and metafunc objects
-------------------------------------------
metafunc objects are passed to the ``pytest_generate_tests`` hook.
They help to inspect a testfunction and to generate tests
according to test configuration or values specified
in the class or module where a test function is defined:
``metafunc.funcargnames``: set of required function arguments for given function
``metafunc.function``: underlying python test function
``metafunc.cls``: class object where the test function is defined in or None.
``metafunc.module``: the module object where the test function is defined in.
``metafunc.config``: access to command line opts and general config
.. _`metafunc.addcall`:
the ``metafunc.addcall()`` method
-----------------------------------------------
.. sourcecode:: python
def addcall(funcargs={}, id=None, param=None):
""" trigger a new test function call. """
``funcargs`` can be a dictionary of argument names
mapped to values - providing it is called *direct parametrization*.
If you provide an `id`` it will be used for reporting
and identification purposes. If you don't supply an `id`
the stringified counter of the list of added calls will be used.
``id`` values needs to be unique between all
invocations for a given test function.
``param`` if specified will be seen by any
`funcarg factory`_ as a ``request.param`` attribute.
Setting it is called *indirect parametrization*.
Indirect parametrization is preferable if test values are
expensive to setup or can only be created in certain environments.
Test generators and thus ``addcall()`` invocations are performed
during test collection which is separate from the actual test
setup and test run phase. With distributed testing collection
and test setup/run happens in different process.
.. _`tutorial examples`:
Tutorial Examples
=======================================
To see how you can implement custom paramtrization schemes,
see e.g. `parametrizing tests, generalized`_ (blog post).
To enable creation of test support code that can flexibly
register setup/teardown functions see the `blog post about
the monkeypatch funcarg`_.
If you find issues or have further suggestions for improving
the mechanism you are welcome to checkout `contact possibilities`_ page.
.. _`application setup tutorial example`:
.. _appsetup:
@ -274,262 +532,3 @@ methods in a convenient way.
.. _`py.path.local`: ../path.html#local
.. _`conftest plugin`: customize.html#conftestplugin
.. _`funcarg factory`:
.. _factory:
funcarg factories: setting up test function arguments
==============================================================
Test functions can specify one ore more arguments ("funcargs")
and a test module or plugin can define functions that provide
the function argument. Let's look at a simple self-contained
example that you can put into a test module:
.. sourcecode:: python
# ./test_simplefactory.py
def pytest_funcarg__myfuncarg(request):
return 42
def test_function(myfuncarg):
assert myfuncarg == 17
If you run this with ``py.test test_simplefactory.py`` you see something like this:
.. sourcecode:: python
=========================== test session starts ============================
python: platform linux2 -- Python 2.6.2
test object 1: /home/hpk/hg/py/trunk/example/funcarg/test_simplefactory.py
test_simplefactory.py F
================================ FAILURES ==================================
______________________________ test_function _______________________________
myfuncarg = 42
def test_function(myfuncarg):
> assert myfuncarg == 17
E assert 42 == 17
test_simplefactory.py:6: AssertionError
======================== 1 failed in 0.11 seconds ==========================
This means that the test function got executed and the assertion failed.
Here is how py.test comes to execute this test function:
1. py.test discovers the ``test_function`` because of the ``test_`` prefix.
The test function needs a function argument named ``myfuncarg``.
A matching factory function is discovered by looking for the special
name ``pytest_funcarg__myfuncarg``.
2. ``pytest_funcarg__myfuncarg(request)`` is called and
returns the value for ``myfuncarg``.
3. ``test_function(42)`` call is executed.
Note that if you misspell a function argument or want
to use one that isn't available, an error with a list of
available function argument is provided.
For more interesting factory functions that make good use of the
`request object`_ please see the `application setup tutorial example`_.
.. _`request object`:
funcarg factory request objects
------------------------------------------
Request objects are passed to funcarg factories and allow
to access test configuration, test context and `useful caching
and finalization helpers`_. Here is a list of attributes:
``request.function``: python function object requesting the argument
``request.cls``: class object where the test function is defined in or None.
``request.module``: module object where the test function is defined in.
``request.config``: access to command line opts and general config
``request.param``: if exists was passed by a previous `metafunc.addcall`_
.. _`useful caching and finalization helpers`:
registering funcarg related finalizers/cleanup
----------------------------------------------------
.. sourcecode:: python
def addfinalizer(func):
""" call a finalizer function when test function finishes. """
Calling ``request.addfinalizer()`` is useful for scheduling teardown
functions. Here is an example for providing a ``myfile``
object that is to be closed when the execution of a
test function finishes.
.. sourcecode:: python
def pytest_funcarg__myfile(self, request):
# ... create and open a unique per-function "myfile" object ...
request.addfinalizer(lambda: myfile.close())
return myfile
managing fixtures across test modules and test runs
----------------------------------------------------------
.. sourcecode:: python
def cached_setup(setup, teardown=None, scope="module", extrakey=None):
""" cache and return result of calling setup().
The requested argument name, the scope and the ``extrakey``
determine the cache key. The scope also determines when
teardown(result) will be called. valid scopes are:
scope == 'function': when the single test function run finishes.
scope == 'module': when tests in a different module are run
scope == 'session': when tests of the session have run.
"""
Calling ``request.cached_setup()`` helps you to manage fixture
objects across several scopes. For example, for creating a Database object
that is to be setup only once during a test session you can use the helper
like this:
.. sourcecode:: python
def pytest_funcarg__database(request):
return request.cached_setup(
setup=lambda: Database("..."),
teardown=lambda val: val.close(),
scope="session"
)
requesting values of other funcargs
---------------------------------------------
.. sourcecode:: python
def getfuncargvalue(name):
""" Lookup and call function argument factory for the given name.
Each function argument is only created once per function setup.
"""
``request.getfuncargvalue(name)`` calls another funcarg factory function.
You can use this function if you want to `decorate a funcarg`_, i.e.
you want to provide the "normal" value but add something
extra. If a factory cannot be found a ``request.Error``
exception will be raised.
.. _`test generators`:
.. _`parametrizing-tests`:
generating parametrized tests
===========================================================
You can parametrize multiple runs of the same test
function by adding new test function calls with different
function argument values. Let's look at a simple self-contained
example:
.. sourcecode:: python
# ./test_example.py
def pytest_generate_tests(metafunc):
if "numiter" in metafunc.funcargnames:
for i in range(10):
metafunc.addcall(funcargs=dict(numiter=i))
def test_func(numiter):
assert numiter < 9
If you run this with ``py.test test_example.py`` you'll get:
.. sourcecode:: python
============================= test session starts ==========================
python: platform linux2 -- Python 2.6.2
test object 1: /home/hpk/hg/py/trunk/test_example.py
test_example.py .........F
================================ FAILURES ==================================
__________________________ test_func.test_func[9] __________________________
numiter = 9
def test_func(numiter):
> assert numiter < 9
E assert 9 < 9
/home/hpk/hg/py/trunk/test_example.py:10: AssertionError
Here is what happens in detail:
1. ``pytest_generate_tests(metafunc)`` hook is called once for each test
function. It adds ten new function calls with explicit function arguments.
2. **execute tests**: ``test_func(numiter)`` is called ten times with
ten different arguments.
.. _`metafunc object`:
test generators and metafunc objects
-------------------------------------------
metafunc objects are passed to the ``pytest_generate_tests`` hook.
They help to inspect a testfunction and to generate tests
according to test configuration or values specified
in the class or module where a test function is defined:
``metafunc.funcargnames``: set of required function arguments for given function
``metafunc.function``: underlying python test function
``metafunc.cls``: class object where the test function is defined in or None.
``metafunc.module``: the module object where the test function is defined in.
``metafunc.config``: access to command line opts and general config
.. _`metafunc.addcall`:
the ``metafunc.addcall()`` method
-----------------------------------------------
.. sourcecode:: python
def addcall(funcargs={}, id=None, param=None):
""" trigger a new test function call. """
``funcargs`` can be a dictionary of argument names
mapped to values - providing it is called *direct parametrization*.
If you provide an `id`` it will be used for reporting
and identification purposes. If you don't supply an `id`
the stringified counter of the list of added calls will be used.
``id`` values needs to be unique between all
invocations for a given test function.
``param`` if specified will be seen by any
`funcarg factory`_ as a ``request.param`` attribute.
Setting it is called *indirect parametrization*.
Indirect parametrization is preferable if test values are
expensive to setup or can only be created in certain environments.
Test generators and thus ``addcall()`` invocations are performed
during test collection which is separate from the actual test
setup and test run phase. With distributed testing collection
and test setup/run happens in different process.

View File

@ -0,0 +1,10 @@
pytest_xmlresult plugin (EXTERNAL)
==========================================
This plugin allows to write results in an XML format
compatible to CruiseControl_, see here for download:
http://github.com/rozza/py.test-plugins
.. _CruiseControl: http://cruisecontrol.sourceforge.net/

View File

@ -1,63 +1,63 @@
plugins for Python test functions
=================================
advanced python testing
=======================
skipping_ advanced skipping for python test functions, classes or modules.
mark_ generic mechanism for marking python functions.
pdb_ interactive debugging with the Python Debugger.
figleaf_ write and report coverage data with 'figleaf'.
coverage_ (3rd) for testing with Ned's coverage module
monkeypatch_ safely patch object attributes, dicts and environment variables.
capture_ configurable per-test stdout/stderr capturing mechanisms.
recwarn_ helpers for asserting deprecation and other warnings.
tmpdir_ provide temporary directories to test functions.
plugins for other testing styles and languages
==============================================
oejskit_ run javascript tests in real life browsers
testing domains
===============
oejskit_ (3rd) run javascript tests in real life browsers
django_ (3rd) for testing django applications
reporting and failure logging
=============================
pastebin_ submit failure or test session information to a pastebin service.
xmlresult_ (3rd) for generating xml reports and CruiseControl integration
resultlog_ resultlog plugin for machine-readable logging of test results.
terminal_ Implements terminal reporting of the full testing process.
other testing conventions
=========================
unittest_ automatically discover and run traditional "unittest.py" style tests.
nose_ nose-compatibility plugin: allow to run nose test suites natively.
django_ support for testing django applications
doctest_ collect and execute doctests from modules and test files.
restdoc_ perform ReST syntax, local and remote reference tests on .rst/.txt files.
plugins for generic reporting and failure logging
=================================================
pastebin_ submit failure or test session information to a pastebin service.
resultlog_ resultlog plugin for machine-readable logging of test results.
terminal_ Implements terminal reporting of the full testing process.
plugins for generic reporting and failure logging
=================================================
pastebin_ submit failure or test session information to a pastebin service.
resultlog_ resultlog plugin for machine-readable logging of test results.
terminal_ Implements terminal reporting of the full testing process.
misc plugins / core functionality
=================================
core debugging / help functionality
===================================
helpconfig_ provide version info, conftest/environment config names.
pdb_ interactive debugging with the Python Debugger.
mark_ generic mechanism for marking python functions.
hooklog_ log invocations of extension hooks to a file.

View File

@ -1,38 +1,42 @@
.. _`helpconfig`: helpconfig.html
.. _`terminal`: terminal.html
.. _`pytest_recwarn.py`: http://bitbucket.org/hpk42/py-trunk/raw/trunk/_py/test/plugin/pytest_recwarn.py
.. _`pytest_recwarn.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.1.0/py/plugin/pytest_recwarn.py
.. _`unittest`: unittest.html
.. _`pytest_monkeypatch.py`: http://bitbucket.org/hpk42/py-trunk/raw/trunk/_py/test/plugin/pytest_monkeypatch.py
.. _`pytest_monkeypatch.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.1.0/py/plugin/pytest_monkeypatch.py
.. _`pastebin`: pastebin.html
.. _`skipping`: skipping.html
.. _`plugins`: index.html
.. _`pytest_doctest.py`: http://bitbucket.org/hpk42/py-trunk/raw/trunk/_py/test/plugin/pytest_doctest.py
.. _`capture`: capture.html
.. _`pytest_nose.py`: http://bitbucket.org/hpk42/py-trunk/raw/trunk/_py/test/plugin/pytest_nose.py
.. _`pytest_restdoc.py`: http://bitbucket.org/hpk42/py-trunk/raw/trunk/_py/test/plugin/pytest_restdoc.py
.. _`restdoc`: restdoc.html
.. _`pytest_pastebin.py`: http://bitbucket.org/hpk42/py-trunk/raw/trunk/_py/test/plugin/pytest_pastebin.py
.. _`mark`: mark.html
.. _`pytest_figleaf.py`: http://bitbucket.org/hpk42/py-trunk/raw/trunk/_py/test/plugin/pytest_figleaf.py
.. _`pytest_hooklog.py`: http://bitbucket.org/hpk42/py-trunk/raw/trunk/_py/test/plugin/pytest_hooklog.py
.. _`pytest_skipping.py`: http://bitbucket.org/hpk42/py-trunk/raw/trunk/_py/test/plugin/pytest_skipping.py
.. _`tmpdir`: tmpdir.html
.. _`pytest_doctest.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.1.0/py/plugin/pytest_doctest.py
.. _`capture`: capture.html
.. _`pytest_nose.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.1.0/py/plugin/pytest_nose.py
.. _`pytest_restdoc.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.1.0/py/plugin/pytest_restdoc.py
.. _`restdoc`: restdoc.html
.. _`pytest_pastebin.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.1.0/py/plugin/pytest_pastebin.py
.. _`pytest_tmpdir.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.1.0/py/plugin/pytest_tmpdir.py
.. _`pytest_figleaf.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.1.0/py/plugin/pytest_figleaf.py
.. _`pytest_hooklog.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.1.0/py/plugin/pytest_hooklog.py
.. _`pytest_skipping.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.1.0/py/plugin/pytest_skipping.py
.. _`checkout the py.test development version`: ../../install.html#checkout
.. _`pytest_helpconfig.py`: http://bitbucket.org/hpk42/py-trunk/raw/trunk/_py/test/plugin/pytest_helpconfig.py
.. _`pytest_helpconfig.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.1.0/py/plugin/pytest_helpconfig.py
.. _`oejskit`: oejskit.html
.. _`doctest`: doctest.html
.. _`pytest_mark.py`: http://bitbucket.org/hpk42/py-trunk/raw/trunk/_py/test/plugin/pytest_mark.py
.. _`pytest_mark.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.1.0/py/plugin/pytest_mark.py
.. _`get in contact`: ../../contact.html
.. _`pytest_capture.py`: http://bitbucket.org/hpk42/py-trunk/raw/trunk/_py/test/plugin/pytest_capture.py
.. _`pytest_capture.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.1.0/py/plugin/pytest_capture.py
.. _`figleaf`: figleaf.html
.. _`customize`: ../customize.html
.. _`hooklog`: hooklog.html
.. _`pytest_terminal.py`: http://bitbucket.org/hpk42/py-trunk/raw/trunk/_py/test/plugin/pytest_terminal.py
.. _`pytest_terminal.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.1.0/py/plugin/pytest_terminal.py
.. _`recwarn`: recwarn.html
.. _`pytest_pdb.py`: http://bitbucket.org/hpk42/py-trunk/raw/trunk/_py/test/plugin/pytest_pdb.py
.. _`pytest_pdb.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.1.0/py/plugin/pytest_pdb.py
.. _`monkeypatch`: monkeypatch.html
.. _`coverage`: coverage.html
.. _`resultlog`: resultlog.html
.. _`django`: django.html
.. _`pytest_unittest.py`: http://bitbucket.org/hpk42/py-trunk/raw/trunk/_py/test/plugin/pytest_unittest.py
.. _`xmlresult`: xmlresult.html
.. _`pytest_unittest.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.1.0/py/plugin/pytest_unittest.py
.. _`nose`: nose.html
.. _`pytest_resultlog.py`: http://bitbucket.org/hpk42/py-trunk/raw/trunk/_py/test/plugin/pytest_resultlog.py
.. _`pytest_resultlog.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.1.0/py/plugin/pytest_resultlog.py
.. _`pdb`: pdb.html

View File

@ -0,0 +1,39 @@
pytest_tmpdir plugin
====================
provide temporary directories to test functions.
.. contents::
:local:
usage example::
def test_plugin(tmpdir):
tmpdir.join("hello").write("hello")
.. _`py.path.local`: ../../path.html
.. _`tmpdir funcarg`:
the 'tmpdir' test function argument
-----------------------------------
return a temporary directory path object
unique to each test function invocation,
created as a sub directory of the base temporary
directory. The returned object is a `py.path.local`_
path object.
Start improving this plugin in 30 seconds
=========================================
1. Download `pytest_tmpdir.py`_ plugin source code
2. put it somewhere as ``pytest_tmpdir.py`` into your import path
3. a subsequent ``py.test`` run will use your local version
Checkout customize_, other plugins_ or `get in contact`_.
.. include:: links.txt

View File

@ -0,0 +1,6 @@
pytest_coverage plugin (EXTERNAL)
==========================================
This plugin allows to use Ned's coverage package, see
http://github.com/rozza/py.test-plugins

View File

@ -7,7 +7,7 @@ Quickstart
.. _here: ../install.html
If you have a version of ``easy_install`` (otherwise see here_) just type::
If you have any ``easy_install`` (otherwise see here_) just type::
easy_install -U py

View File

@ -1,14 +1,20 @@
====================================
xUnit style setup
extended xUnit style setup
====================================
.. _`funcargs`: funcargs.html
.. _`test parametrization`: funcargs.html#parametrizing-tests
.. _`unittest plugin`: plugin/unittest.html
.. _`xUnit`: http://en.wikipedia.org/wiki/XUnit
Note:
Since version 1.0 funcargs_ present the recommended way
to manage flexible and scalable test setups.
Since version 1.0 funcargs_ present the new and
more powerful way to manage test setups with larger
test suites. *funcargs* also provide flexible
`test parametrization`_ which goes way beyond
what you can do with the xUnit setup/teardown-method
patter.
Python, Java and many other languages have a tradition
of using xUnit_ style testing. This typically
@ -19,6 +25,10 @@ scopes for which you can provide setup/teardown
hooks to provide test fixtures: per-module, per-class
and per-method/function. ``py.test`` will
discover and call according methods automatically.
The `unittest plugin`_ also will intregate ``unittest.TestCase``
instances into a test run and call respective setup/teardown methods.
All setup/teardown methods are optional.
The following methods are called at module level if they exist:

View File

@ -2,20 +2,14 @@
"""
py.test and pylib: rapid testing and development utils
- `py.test`_: cross-project testing tool with many advanced features
- `py.path`_: path abstractions over local and subversion files
- `py.code`_: dynamic code compile and traceback printing support
Compatibility: Linux, Win32, OSX, Python versions 2.4 through to 3.1.
For questions please check out http://pylib.org/contact.html
.. _`py.test`: http://pylib.org/test.html
.. _`py.path`: http://pylib.org/path.html
.. _`py.code`: http://pylib.org/html
this module uses apipkg.py for lazy-loading sub modules
and classes. The initpkg-dictionary below specifies
name->value mappings where value can be another namespace
dictionary or an import path.
(c) Holger Krekel and others, 2009
"""
version = "trunk"
version = "1.1.0"
__version__ = version = version or "1.1.x"
import py.apipkg

View File

@ -46,6 +46,7 @@ class Parser:
self._groups.insert(i+1, group)
return group
addgroup = getgroup
def addgroup(self, name, description=""):
py.log._apiwarn("1.1", "use getgroup() which gets-or-creates")
return self.getgroup(name, description)

View File

@ -70,7 +70,7 @@ def pytest_addoption(parser):
add_dist_options(parser)
else:
parser.epilog = (
"execnet missing: --looponfailing and distributed testing not available.")
"'execnet' package required for --looponfailing / distributed testing.")
def add_dist_options(parser):
# see http://pytest.org/help/dist")

View File

@ -83,7 +83,7 @@ skipping on a missing import dependency
--------------------------------------------------
You can use the following import helper at module level
or within a test or setup function.
or within a test or test setup function::
docutils = py.test.importorskip("docutils")

View File

@ -259,8 +259,8 @@ class TerminalReporter:
verinfo = ".".join(map(str, sys.version_info[:3]))
msg = "python: platform %s -- Python %s" % (sys.platform, verinfo)
msg += " -- pytest-%s" % (py.__version__)
if self.config.option.verbose or self.config.option.debug or getattr(self.config.option, 'pastebin', None):
msg += " -- pytest-%s" % (py.__version__)
msg += " -- " + str(sys.executable)
self.write_line(msg)

View File

@ -1,16 +1,21 @@
"""
provide temporary directories to test functions and methods.
"""provide temporary directories to test functions.
example:
pytest_plugins = "pytest_tmpdir"
usage example::
def test_plugin(tmpdir):
tmpdir.join("hello").write("hello")
.. _`py.path.local`: ../../path.html
"""
import py
def pytest_funcarg__tmpdir(request):
"""return a temporary directory path object
unique to each test function invocation,
created as a sub directory of the base temporary
directory. The returned object is a `py.path.local`_
path object.
"""
name = request.function.__name__
return request.config.mktemp(name, numbered=True)

View File

@ -28,7 +28,7 @@ def main():
name='py',
description='py.test and pylib: rapid testing and development utils.',
long_description = long_description,
version= trunk or '1.1.0b1',
version= trunk or '1.1.0',
url='http://pylib.org',
license='MIT license',
platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'],
@ -42,7 +42,7 @@ def main():
'py.svnwcrevert = py.cmdline:pysvnwcrevert',
'py.test = py.cmdline:pytest',
'py.which = py.cmdline:pywhich']},
classifiers=['Development Status :: 4 - Beta',
classifiers=['Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
'Operating System :: POSIX',
@ -50,7 +50,6 @@ def main():
'Operating System :: MacOS :: MacOS X',
'Topic :: Software Development :: Testing',
'Topic :: Software Development :: Libraries',
'Topic :: System :: Distributed Computing',
'Topic :: Utilities',
'Programming Language :: Python'],
packages=['py',

View File

@ -118,7 +118,7 @@ class TestLogConsumer:
def test_log_file(self):
customlog = tempdir.join('log.out')
py.log.setconsumer("default", open(str(customlog), 'w', buffering=0))
py.log.setconsumer("default", open(str(customlog), 'w', buffering=1))
py.log.Producer("default")("hello world #1")
assert customlog.readlines() == ['[default] hello world #1\n']