commit
6dd3807914
|
@ -11,6 +11,8 @@ provides a number of implementations of this API.
|
||||||
Path implementations provided by :api:`py.path`
|
Path implementations provided by :api:`py.path`
|
||||||
===============================================
|
===============================================
|
||||||
|
|
||||||
|
.. _`local`:
|
||||||
|
|
||||||
:api:`py.path.local`
|
:api:`py.path.local`
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
|
|
|
@ -21,3 +21,26 @@ behind the ``--`` double-dash.
|
||||||
IOW, you can set default values for options per project, per
|
IOW, you can set default values for options per project, per
|
||||||
home-directoray, per shell session or per test-run.
|
home-directoray, per shell session or per test-run.
|
||||||
|
|
||||||
|
|
||||||
|
.. _`basetemp`:
|
||||||
|
|
||||||
|
per-testrun temporary directories
|
||||||
|
-------------------------------------------
|
||||||
|
|
||||||
|
``py.test`` runs provide means to create per-test session
|
||||||
|
temporary (sub) directories. You can create such directories
|
||||||
|
like this:
|
||||||
|
|
||||||
|
.. sourcecode: python
|
||||||
|
|
||||||
|
import py
|
||||||
|
basetemp = py.test.config.ensuretemp()
|
||||||
|
basetemp_subdir = py.test.config.ensuretemp("subdir")
|
||||||
|
|
||||||
|
By default, ``py.test`` creates a ``pytest-NUMBER`` directory
|
||||||
|
and will keep around the directories of the last three
|
||||||
|
test runs. You can also set the base temporary directory
|
||||||
|
with the `--basetemp`` option. When distributing
|
||||||
|
tests on the same machine, ``py.test`` takes care to
|
||||||
|
pass around the basetemp directory such that all temporary
|
||||||
|
files land below the same basetemp directory.
|
||||||
|
|
|
@ -1,7 +1,20 @@
|
||||||
|
======================================
|
||||||
|
Writing plugins and extensions
|
||||||
|
======================================
|
||||||
|
|
||||||
|
|
||||||
|
.. _`local plugin`:
|
||||||
|
|
||||||
|
Local Plugins
|
||||||
|
==================================
|
||||||
|
|
||||||
|
You can easily specify a project-specific or "local"
|
||||||
|
plugin by defining a ``ConftestPlugin`` in a ``conftest.py``
|
||||||
|
file like this::
|
||||||
|
|
||||||
|
class ConftestPlugin:
|
||||||
|
""" my local plugin. """
|
||||||
|
|
||||||
===============
|
|
||||||
Writing plugins
|
|
||||||
===============
|
|
||||||
|
|
||||||
Learning by examples
|
Learning by examples
|
||||||
=====================
|
=====================
|
||||||
|
|
|
@ -36,8 +36,19 @@ or class with a leading ``Test`` name is collected.
|
||||||
Rapidly write integration, functional, unit tests
|
Rapidly write integration, functional, unit tests
|
||||||
===================================================
|
===================================================
|
||||||
|
|
||||||
py.test provides
|
XXX
|
||||||
|
|
||||||
|
funcargs and xUnit style setups
|
||||||
|
===================================================
|
||||||
|
|
||||||
|
py.test provides powerful means for managing test
|
||||||
|
state and fixtures. Apart from the `traditional
|
||||||
|
xUnit style setup`_ for unittests it features the
|
||||||
|
simple and powerful `funcargs mechanism`_ for handling
|
||||||
|
both complex and simple test scenarious.
|
||||||
|
|
||||||
|
.. _`funcargs mechanism`: funcargs.html
|
||||||
|
.. _`traditional xUnit style setup`: xunit_setup.html
|
||||||
|
|
||||||
load-balance tests to multiple CPUs
|
load-balance tests to multiple CPUs
|
||||||
===================================
|
===================================
|
||||||
|
|
|
@ -1,142 +1,314 @@
|
||||||
|
======================================================
|
||||||
|
**funcargs**: powerful and simple test setup
|
||||||
|
======================================================
|
||||||
|
|
||||||
=====================================
|
In version 1.0 py.test introduces a new mechanism for setting up test
|
||||||
Python test function arguments
|
state for use by Python test functions. It is particularly useful
|
||||||
=====================================
|
for functional and integration testing but also for unit testing.
|
||||||
|
Using funcargs you can easily:
|
||||||
|
|
||||||
py.test enables a new way to separate test configuration
|
* write self-contained, simple to read and debug test functions
|
||||||
and test setup from actual test code in test functions.
|
* cleanly encapsulate glue code between your app and your tests
|
||||||
When it runs a test functions it will lookup function
|
* setup test state depending on command line options or environment
|
||||||
arguments by name and provide a value.
|
|
||||||
Here is a simple example for such a test function:
|
|
||||||
|
|
||||||
def test_function(mysetup):
|
Using the funcargs mechanism will increase readability
|
||||||
# work with mysetup
|
and allow for easier refactoring of your application
|
||||||
|
and its test suites.
|
||||||
|
|
||||||
To provide a value py.test looks for a ``pytest_funcargs``
|
.. contents:: Contents:
|
||||||
dictionary in the test module, for example::
|
:depth: 2
|
||||||
|
|
||||||
class MySetup:
|
The basic funcarg request/provide mechanism
|
||||||
def __init__(self, pyfuncitem):
|
=============================================
|
||||||
self.pyfuncitem = pyfuncitem
|
|
||||||
pytest_funcargs = {'mysetup': MySetup}
|
|
||||||
|
|
||||||
This is already enough to run the test. Of course
|
To use funcargs you only need to specify
|
||||||
up until now our ``mysetup`` does not provide
|
a named argument for your test function:
|
||||||
much value. But it is now easy to add new
|
|
||||||
methods on the ``MySetup`` class that have
|
|
||||||
full access to the test collection process.
|
|
||||||
|
|
||||||
Plugins can register their funcargs via
|
.. sourcecode:: python
|
||||||
the config object, usually upon initial configure::
|
|
||||||
|
def test_function(myarg):
|
||||||
|
# use myarg
|
||||||
|
|
||||||
|
For each test function that requests this ``myarg``
|
||||||
|
argument a matching so called funcarg provider
|
||||||
|
will be invoked. A Funcarg provider for ``myarg``
|
||||||
|
is written down liks this:
|
||||||
|
|
||||||
|
.. sourcecode:: python
|
||||||
|
|
||||||
|
def pytest_funcarg__myarg(self, request):
|
||||||
|
# return value for myarg here
|
||||||
|
|
||||||
|
Such a provider method can live on a test class,
|
||||||
|
test module or on a local or global plugin.
|
||||||
|
The method is recognized by the ``pytest_funcarg__``
|
||||||
|
prefix and is correlated to the argument
|
||||||
|
name which follows this prefix. The passed in
|
||||||
|
``request`` object allows to interact
|
||||||
|
with test configuration, test collection
|
||||||
|
and test running aspects.
|
||||||
|
|
||||||
|
.. _`request object`:
|
||||||
|
|
||||||
|
funcarg request objects
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
Request objects encapsulate a request for a function argument from a
|
||||||
|
specific test function. Request objects provide access to command line
|
||||||
|
options, the underlying python function and allow interaction
|
||||||
|
with other providers and the test running process.
|
||||||
|
|
||||||
|
Attributes of request objects
|
||||||
|
++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
``request.argname``: name of the requested function argument
|
||||||
|
|
||||||
|
``request.function``: python function object requesting the argument
|
||||||
|
|
||||||
|
``request.fspath``: filesystem path of containing module
|
||||||
|
|
||||||
|
``request.config``: access to command line opts and general config
|
||||||
|
|
||||||
|
finalizing after test function executed
|
||||||
|
++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
Request objects allow to **register a finalizer method** which is
|
||||||
|
called after a test function has finished running.
|
||||||
|
This is useful for tearing down or cleaning up
|
||||||
|
test state. Here is a basic example for providing
|
||||||
|
a ``myfile`` object that will be closed upon test
|
||||||
|
function finish:
|
||||||
|
|
||||||
|
.. sourcecode:: python
|
||||||
|
|
||||||
|
def pytest_funcarg__myfile(self, request):
|
||||||
|
# ... create and open a "myfile" object ...
|
||||||
|
request.addfinalizer(lambda: myfile.close())
|
||||||
|
return myfile
|
||||||
|
|
||||||
|
a unique temporary directory
|
||||||
|
++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
request objects allow to create unique temporary
|
||||||
|
directories. These directories will be created
|
||||||
|
as subdirectories under the `per-testsession
|
||||||
|
temporary directory`_. Each request object
|
||||||
|
receives its own unique subdirectory whose
|
||||||
|
basenames starts with the name of the function
|
||||||
|
that triggered the funcarg request. You
|
||||||
|
can further work with the provided `py.path.local`_
|
||||||
|
object to e.g. create subdirs or config files::
|
||||||
|
|
||||||
|
def pytest_funcarg__mysetup(self, request):
|
||||||
|
tmpdir = request.maketempdir()
|
||||||
|
tmpdir.mkdir("mysubdir")
|
||||||
|
tmpdir.join("config.ini").write("[default")
|
||||||
|
return tmpdir
|
||||||
|
|
||||||
|
Note that you do not need to perform finalization,
|
||||||
|
i.e. remove the temporary directory as this is
|
||||||
|
part of the global management of the base temporary
|
||||||
|
directory.
|
||||||
|
|
||||||
|
.. _`per-testsession temporary directory`: config.html#basetemp
|
||||||
|
|
||||||
|
decorating/adding to existing funcargs
|
||||||
|
++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
If you want to **decorate a function argument** that is
|
||||||
|
provided elsewhere you can ask the request object
|
||||||
|
to provide the "next" value:
|
||||||
|
|
||||||
|
.. sourcecode:: python
|
||||||
|
|
||||||
|
def pytest_funcarg__myfile(self, request):
|
||||||
|
myfile = request.call_next_provider()
|
||||||
|
# do something extra
|
||||||
|
return myfile
|
||||||
|
|
||||||
|
This will raise a ``request.Error`` exception if there
|
||||||
|
is no next provider left. See the `decorator example`_
|
||||||
|
for a use of this method.
|
||||||
|
|
||||||
|
|
||||||
|
.. _`lookup order`:
|
||||||
|
|
||||||
|
Order of funcarg provider lookup
|
||||||
|
----------------------------------------
|
||||||
|
|
||||||
|
For any funcarg argument request here is the
|
||||||
|
lookup order for provider methods:
|
||||||
|
|
||||||
|
1. test class (if we are executing a method)
|
||||||
|
2. test module
|
||||||
|
3. local plugins
|
||||||
|
4. global plugins
|
||||||
|
|
||||||
|
|
||||||
|
Using multiple funcargs
|
||||||
|
----------------------------------------
|
||||||
|
|
||||||
|
A test function may receive more than one
|
||||||
|
function arguments. For each of the
|
||||||
|
function arguments a lookup of a
|
||||||
|
matching provider will be performed.
|
||||||
|
|
||||||
|
|
||||||
|
Funcarg Examples
|
||||||
|
=====================
|
||||||
|
|
||||||
|
Example: basic application specific setup
|
||||||
|
-----------------------------------------------------
|
||||||
|
|
||||||
|
Here is a basic useful example for handling application
|
||||||
|
specific setup. The goal is to have one place where
|
||||||
|
we have the glue code for bootstrapping and configuring
|
||||||
|
application objects and allow test modules and
|
||||||
|
test functions to stay ignorant of involved details.
|
||||||
|
Let's start with the using side and consider a simple
|
||||||
|
test function living in a test file ``test_sample.py``:
|
||||||
|
|
||||||
|
.. sourcecode:: python
|
||||||
|
|
||||||
|
def test_answer(mysetup):
|
||||||
|
app = mysetup.myapp()
|
||||||
|
answer = app.question()
|
||||||
|
assert answer == 42
|
||||||
|
|
||||||
|
To run this test py.test looks up and calls a provider to obtain the
|
||||||
|
required ``mysetup`` function argument. The test function simply
|
||||||
|
interacts with the provided application specific setup.
|
||||||
|
|
||||||
|
To provide the ``mysetup`` function argument we write down
|
||||||
|
a provider method in a `local plugin`_ by putting the
|
||||||
|
following code into a local ``conftest.py``:
|
||||||
|
|
||||||
|
.. sourcecode:: python
|
||||||
|
|
||||||
|
from myapp import MyApp
|
||||||
|
|
||||||
class ConftestPlugin:
|
class ConftestPlugin:
|
||||||
def pytest_configure(self, config):
|
def pytest_funcarg__mysetup(self, request):
|
||||||
config.register_funcargs(mysetup=MySetup)
|
return MySetup()
|
||||||
|
|
||||||
|
class MySetup:
|
||||||
|
def myapp(self):
|
||||||
|
return MyApp()
|
||||||
|
|
||||||
|
The ``pytest_funcarg__mysetup`` method is called to
|
||||||
|
provide a value for the requested ``mysetup`` test function argument.
|
||||||
|
To complete the example we put a pseudo MyApp object
|
||||||
|
into ``myapp.py``:
|
||||||
|
|
||||||
|
.. sourcecode:: python
|
||||||
|
|
||||||
|
class MyApp:
|
||||||
|
def question(self):
|
||||||
|
return 6 * 9
|
||||||
|
|
||||||
|
You can now run the test with ``py.test test_sample.py`` which will
|
||||||
|
show this failure:
|
||||||
|
|
||||||
|
.. sourcecode:: python
|
||||||
|
|
||||||
|
def test_answer(mysetup):
|
||||||
|
app = mysetup.myapp()
|
||||||
|
answer = app.question()
|
||||||
|
> assert answer == 42
|
||||||
|
E assert 54 == 42
|
||||||
|
|
||||||
|
If you are confused as to that the question or answer is
|
||||||
|
here, please visit here_.
|
||||||
|
|
||||||
|
.. _here: http://uncyclopedia.wikia.com/wiki/The_Hitchhiker's_Guide_to_the_Galaxy
|
||||||
|
|
||||||
|
.. _`local plugin`: ext.html#local-plugin
|
||||||
|
|
||||||
|
|
||||||
|
Example: specifying funcargs in test modules or classes
|
||||||
|
---------------------------------------------------------
|
||||||
|
|
||||||
|
.. sourcecode:: python
|
||||||
|
|
||||||
|
def pytest_funcarg__mysetup(request):
|
||||||
|
result = request.call_next_provider()
|
||||||
|
result.extra = "..."
|
||||||
|
return result
|
||||||
|
|
||||||
|
You can also put such a function into a test class like this:
|
||||||
|
|
||||||
|
.. sourcecode:: python
|
||||||
|
|
||||||
|
class TestClass:
|
||||||
|
def pytest_funcarg__mysetup(self, request):
|
||||||
|
# ...
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
Example: command line option for providing SSH-host
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
If you provide a "funcarg" from a plugin you can
|
If you provide a "funcarg" from a plugin you can
|
||||||
easily make methods depend on command line options
|
easily make methods depend on command line options
|
||||||
or environment settings. Here is a complete
|
or environment settings. Here is a complete
|
||||||
example that allows to run tests involving
|
example that allows to run tests involving
|
||||||
an SSH connection if an ssh host is specified::
|
an SSH connection if an ssh host is specified:
|
||||||
|
|
||||||
|
.. sourcecode:: python
|
||||||
|
|
||||||
class ConftestPlugin:
|
class ConftestPlugin:
|
||||||
def pytest_addoption(self, parser):
|
def pytest_addoption(self, parser):
|
||||||
parser.addoption("--ssh", action="store", default=None,
|
parser.addoption("--ssh", action="store", default=None,
|
||||||
help="specify ssh host to run tests with")
|
help="specify ssh host to run tests with")
|
||||||
|
|
||||||
def pytest_configure(self, config):
|
pytest_funcarg__mysetup = MySetupFuncarg
|
||||||
config.register_funcargs(mysetup=MySetup)
|
|
||||||
|
|
||||||
class MySetup:
|
class MySetupFuncarg:
|
||||||
def __init__(self, pyfuncitem):
|
def __init__(self, request):
|
||||||
self.pyfuncitem = pyfuncitem
|
self.request = request
|
||||||
def ssh_gateway(self):
|
def ssh_gateway(self):
|
||||||
host = pyfuncitem.config.option.ssh
|
host = self.request.config.option.ssh
|
||||||
if host is None:
|
if host is None:
|
||||||
py.test.skip("specify ssh host with --ssh to run this test")
|
py.test.skip("specify ssh host with --ssh to run this test")
|
||||||
return py.execnet.SshGateway(host)
|
return py.execnet.SshGateway(host)
|
||||||
|
|
||||||
Now any test functions can use the "mysetup" object, for example::
|
Now any test functions can use the "mysetup.ssh_gateway()" method like this:
|
||||||
|
|
||||||
|
.. sourcecode:: python
|
||||||
|
|
||||||
class TestClass:
|
class TestClass:
|
||||||
def test_function(self, mysetup):
|
def test_function(self, mysetup):
|
||||||
ssh_gw = mysetup.ssh_gateway()
|
ssh_gw = mysetup.ssh_gateway()
|
||||||
# work with ssh_gw
|
# work with ssh_gw
|
||||||
|
|
||||||
Without specifying a command line option the output looks like this::
|
Running this without the command line will yield this run result::
|
||||||
|
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
Lookup rules
|
.. _`accept example`:
|
||||||
======================
|
|
||||||
|
|
||||||
In order to run this test function a value for the
|
example: specifying and selecting acceptance tests
|
||||||
``mysetup`` needs to be found. Here is how py.test
|
--------------------------------------------------------------
|
||||||
finds a matching provider function:
|
|
||||||
|
|
||||||
1. see if there is a ``pytest_funcargs`` dictionary
|
.. sourcecode:: python
|
||||||
which maps ``mysetup`` to a provider function.
|
|
||||||
if so, call the provider function.
|
|
||||||
|
|
||||||
XXX
|
class ConftestPlugin:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
example
|
|
||||||
=====================
|
|
||||||
|
|
||||||
You can run a test file ``test_some.py`` with this content:
|
|
||||||
|
|
||||||
pytest_funcargs = {'myarg': (lambda pyfuncitem: 42)}
|
|
||||||
|
|
||||||
def test_something(myarg):
|
|
||||||
assert myarg == 42
|
|
||||||
|
|
||||||
You can also put this on a class:
|
|
||||||
|
|
||||||
class TestClass:
|
|
||||||
pytest_funcargs = {'myarg': (lambda pyfuncitem: 42)}
|
|
||||||
|
|
||||||
def test_something(self, myarg):
|
|
||||||
assert myarg == 42
|
|
||||||
|
|
||||||
To separate funcarg setup you can also put a funcarg
|
|
||||||
definition into a conftest.py::
|
|
||||||
|
|
||||||
pytest_funcargs = {'myarg': decorate_myarg}
|
|
||||||
def decorate_myarg(pyfuncitem):
|
|
||||||
result = pyfuncitem.call_next_provider()
|
|
||||||
return result + 1
|
|
||||||
|
|
||||||
for registering funcargs from a plugin, talk to the
|
|
||||||
test configuration object like this::
|
|
||||||
|
|
||||||
class MyPlugin:
|
|
||||||
def pytest_configure(self, config):
|
|
||||||
config.register_funcargs(
|
|
||||||
myarg=decorate_myarg
|
|
||||||
)
|
|
||||||
|
|
||||||
a local helper funcarg for doing acceptance tests maybe
|
|
||||||
by running shell commands could look like this::
|
|
||||||
|
|
||||||
class MyPlugin:
|
|
||||||
def pytest_option(self, parser):
|
def pytest_option(self, parser):
|
||||||
group = parser.addgroup("myproject acceptance tests")
|
group = parser.getgroup("myproject")
|
||||||
group.addoption("-A", dest="acceptance", action="store_true",
|
group.addoption("-A", dest="acceptance", action="store_true",
|
||||||
help="run (slow) acceptance tests")
|
help="run (slow) acceptance tests")
|
||||||
|
|
||||||
def pytest_configure(self, config):
|
def pytest_funcarg__accept(self, request):
|
||||||
config.register_funcargs(accept=AcceptFuncarg)
|
return AcceptFuncarg(request)
|
||||||
|
|
||||||
class AcceptFuncarg:
|
class AcceptFuncarg:
|
||||||
def __init__(self, pyfuncitem):
|
def __init__(self, request):
|
||||||
if not pyfuncitem.config.option.acceptance:
|
if not request.config.option.acceptance:
|
||||||
py.test.skip("specify -A to run acceptance tests")
|
py.test.skip("specify -A to run acceptance tests")
|
||||||
self.tmpdir = pyfuncitem.config.maketempdir(pyfuncitem.name)
|
self.tmpdir = request.config.maketempdir(request.argname)
|
||||||
self._old = self.tmpdir.chdir()
|
self._old = self.tmpdir.chdir()
|
||||||
pyfuncitem.addfinalizer(self.finalize)
|
request.addfinalizer(self.finalize)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
return py.process.cmdexec("echo hello")
|
return py.process.cmdexec("echo hello")
|
||||||
|
@ -145,16 +317,71 @@ by running shell commands could look like this::
|
||||||
self._old.chdir()
|
self._old.chdir()
|
||||||
# cleanup any other resources
|
# cleanup any other resources
|
||||||
|
|
||||||
|
|
||||||
and the actual test function example:
|
and the actual test function example:
|
||||||
|
|
||||||
|
.. sourcecode:: python
|
||||||
|
|
||||||
def test_some_acceptance_aspect(accept):
|
def test_some_acceptance_aspect(accept):
|
||||||
accept.tmpdir.mkdir("somesub")
|
accept.tmpdir.mkdir("somesub")
|
||||||
result = accept.run()
|
result = accept.run()
|
||||||
assert result
|
assert result
|
||||||
|
|
||||||
for registering funcargs from a plugin, talk to the
|
That's it! This test will get automatically skipped with
|
||||||
test configuration object like this::
|
an appropriate message if you just run ``py.test``::
|
||||||
|
|
||||||
XXX
|
... OUTPUT of py.test on this example ...
|
||||||
|
|
||||||
|
|
||||||
|
.. _`decorator example`:
|
||||||
|
|
||||||
|
example: decorating/extending a funcarg in a TestClass
|
||||||
|
--------------------------------------------------------------
|
||||||
|
|
||||||
|
For larger scale setups it's sometimes useful to decorare
|
||||||
|
a funcarg just for a particular test module or even
|
||||||
|
a particular test class. We can extend the `accept example`_
|
||||||
|
by putting this in our test class:
|
||||||
|
|
||||||
|
.. sourcecode:: python
|
||||||
|
|
||||||
|
class TestSpecialAcceptance:
|
||||||
|
def pytest_funcarg__accept(self, request):
|
||||||
|
arg = request.call_next_provider()
|
||||||
|
# create a special layout in our tempdir
|
||||||
|
arg.tmpdir.mkdir("special")
|
||||||
|
return arg
|
||||||
|
|
||||||
|
def test_sometest(self, accept):
|
||||||
|
assert accept.tmpdir.join("special").check()
|
||||||
|
|
||||||
|
According to the `lookup order`_ our class-specific provider will
|
||||||
|
be invoked first. Here, we just ask our request object to
|
||||||
|
call the next provider and decorate its result. This simple
|
||||||
|
mechanism allows us to stay ignorant of how/where the
|
||||||
|
function argument is provided.
|
||||||
|
|
||||||
|
Note that we make use here of `py.path.local`_ objects
|
||||||
|
that provide uniform access to the local filesystem.
|
||||||
|
|
||||||
|
.. _`py.path.local`: ../path.html#local
|
||||||
|
|
||||||
|
Questions and Answers
|
||||||
|
==================================
|
||||||
|
|
||||||
|
.. _`why pytest_pyfuncarg__ methods?`:
|
||||||
|
|
||||||
|
Why ``pytest_funcarg__*`` methods?
|
||||||
|
------------------------------------
|
||||||
|
|
||||||
|
When experimenting with funcargs we also considered an explicit
|
||||||
|
registration mechanism, i.e. calling a register method e.g. on the
|
||||||
|
config object. But lacking a good use case for this indirection and
|
||||||
|
flexibility we decided to go for `Convention over Configuration`_
|
||||||
|
and allow to directly specify the provider. It has the
|
||||||
|
positive implication that you should be able to
|
||||||
|
"grep" for ``pytest_funcarg__MYARG`` and will find all
|
||||||
|
providing sites (usually exactly one).
|
||||||
|
|
||||||
|
.. _`Convention over Configuration`: http://en.wikipedia.org/wiki/Convention_over_Configuration
|
||||||
|
|
||||||
|
|
|
@ -1,65 +0,0 @@
|
||||||
=================
|
|
||||||
Managing state
|
|
||||||
=================
|
|
||||||
|
|
||||||
funcargs: provding arguments for test functions
|
|
||||||
===========================================================
|
|
||||||
|
|
||||||
XXX write docs
|
|
||||||
|
|
||||||
Managing test state across test modules, classes and methods
|
|
||||||
============================================================
|
|
||||||
|
|
||||||
Often you want to create some files, database connections or other
|
|
||||||
state in order to run tests in a certain environment. With
|
|
||||||
``py.test`` there are three scopes for which you can provide hooks to
|
|
||||||
manage such state. Again, ``py.test`` will detect these hooks in
|
|
||||||
modules on a name basis. The following module-level hooks will
|
|
||||||
automatically be called by the session::
|
|
||||||
|
|
||||||
def setup_module(module):
|
|
||||||
""" setup up any state specific to the execution
|
|
||||||
of the given module.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def teardown_module(module):
|
|
||||||
""" teardown any state that was previously setup
|
|
||||||
with a setup_module method.
|
|
||||||
"""
|
|
||||||
|
|
||||||
The following hooks are available for test classes::
|
|
||||||
|
|
||||||
def setup_class(cls):
|
|
||||||
""" setup up any state specific to the execution
|
|
||||||
of the given class (which usually contains tests).
|
|
||||||
"""
|
|
||||||
|
|
||||||
def teardown_class(cls):
|
|
||||||
""" teardown any state that was previously setup
|
|
||||||
with a call to setup_class.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def setup_method(self, method):
|
|
||||||
""" setup up any state tied to the execution of the given
|
|
||||||
method in a class. setup_method is invoked for every
|
|
||||||
test method of a class.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def teardown_method(self, method):
|
|
||||||
""" teardown any state that was previously setup
|
|
||||||
with a setup_method call.
|
|
||||||
"""
|
|
||||||
|
|
||||||
The last two hooks, ``setup_method`` and ``teardown_method``, are
|
|
||||||
equivalent to ``setUp`` and ``tearDown`` in the Python standard
|
|
||||||
library's ``unitest`` module.
|
|
||||||
|
|
||||||
All setup/teardown methods are optional. You could have a
|
|
||||||
``setup_module`` but no ``teardown_module`` and the other way round.
|
|
||||||
|
|
||||||
Note that while the test session guarantees that for every ``setup`` a
|
|
||||||
corresponding ``teardown`` will be invoked (if it exists) it does
|
|
||||||
*not* guarantee that any ``setup`` is called only happens once. For
|
|
||||||
example, the session might decide to call the ``setup_module`` /
|
|
||||||
``teardown_module`` pair more than once during the execution of a test
|
|
||||||
module.
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
====================================
|
||||||
|
xUnit style setup
|
||||||
|
====================================
|
||||||
|
|
||||||
|
.. _`funcargs`: funcargs.html
|
||||||
|
.. _`xUnit`: http://en.wikipedia.org/wiki/XUnit
|
||||||
|
|
||||||
|
Note:
|
||||||
|
|
||||||
|
Since version 1.0 py.test offers funcargs_ for both
|
||||||
|
simple and complex test setup needs. Especially
|
||||||
|
for functional and integration, but also for unit testing, it is
|
||||||
|
highly recommended that you use this new method.
|
||||||
|
|
||||||
|
Python, Java and other languages have a tradition
|
||||||
|
of using xUnit_ style testing. This typically
|
||||||
|
involves the call of a ``setup`` method before
|
||||||
|
a test function is run and ``teardown`` after
|
||||||
|
it finishes. With ``py.test`` there are three
|
||||||
|
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.
|
||||||
|
All setup/teardown methods are optional.
|
||||||
|
|
||||||
|
The following methods are called at module level if they exist:
|
||||||
|
|
||||||
|
.. sourcecode:: python
|
||||||
|
|
||||||
|
def setup_module(module):
|
||||||
|
""" setup up any state specific to the execution
|
||||||
|
of the given module.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def teardown_module(module):
|
||||||
|
""" teardown any state that was previously setup
|
||||||
|
with a setup_module method.
|
||||||
|
"""
|
||||||
|
|
||||||
|
The following hooks are available for test classes:
|
||||||
|
|
||||||
|
.. sourcecode:: python
|
||||||
|
|
||||||
|
def setup_class(cls):
|
||||||
|
""" setup up any state specific to the execution
|
||||||
|
of the given class (which usually contains tests).
|
||||||
|
"""
|
||||||
|
|
||||||
|
def teardown_class(cls):
|
||||||
|
""" teardown any state that was previously setup
|
||||||
|
with a call to setup_class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def setup_method(self, method):
|
||||||
|
""" setup up any state tied to the execution of the given
|
||||||
|
method in a class. setup_method is invoked for every
|
||||||
|
test method of a class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def teardown_method(self, method):
|
||||||
|
""" teardown any state that was previously setup
|
||||||
|
with a setup_method call.
|
||||||
|
"""
|
||||||
|
|
||||||
|
The last two hooks, ``setup_method`` and ``teardown_method``, are
|
||||||
|
equivalent to ``setUp`` and ``tearDown`` in the Python standard
|
||||||
|
library's `unittest.py module`_.
|
||||||
|
|
||||||
|
Note that it possible that setup/teardown pairs are invoked multiple
|
||||||
|
times per testing process.
|
||||||
|
|
||||||
|
.. _`unittest.py module`: http://docs.python.org/library/unittest.html
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
|
||||||
|
from myapp import MyApp
|
||||||
|
|
||||||
|
class ConftestPlugin:
|
||||||
|
def pytest_funcarg__mysetup(self, request):
|
||||||
|
return MySetup()
|
||||||
|
|
||||||
|
class MySetup:
|
||||||
|
def myapp(self):
|
||||||
|
return MyApp()
|
|
@ -0,0 +1,5 @@
|
||||||
|
|
||||||
|
class MyApp:
|
||||||
|
def question(self):
|
||||||
|
return 6 * 9
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
|
||||||
|
def test_answer(mysetup):
|
||||||
|
app = mysetup.myapp()
|
||||||
|
answer = app.question()
|
||||||
|
assert answer == 42
|
|
@ -90,9 +90,7 @@ class Registry:
|
||||||
l = []
|
l = []
|
||||||
if plugins is None:
|
if plugins is None:
|
||||||
plugins = self._plugins
|
plugins = self._plugins
|
||||||
if extra:
|
for plugin in list(plugins) + list(extra):
|
||||||
plugins += list(extra)
|
|
||||||
for plugin in plugins:
|
|
||||||
try:
|
try:
|
||||||
l.append(getattr(plugin, attrname))
|
l.append(getattr(plugin, attrname))
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
|
|
|
@ -5,10 +5,10 @@ rsyncignore = ['c-extension/greenlet/build']
|
||||||
|
|
||||||
import py
|
import py
|
||||||
class PylibTestconfigPlugin:
|
class PylibTestconfigPlugin:
|
||||||
def pytest_funcarg__specssh(self, pyfuncitem):
|
def pytest_funcarg__specssh(self, request):
|
||||||
return getspecssh(pyfuncitem.config)
|
return getspecssh(request.config)
|
||||||
def pytest_funcarg__specsocket(self, pyfuncitem):
|
def pytest_funcarg__specsocket(self, request):
|
||||||
return getsocketspec(pyfuncitem.config)
|
return getsocketspec(request.config)
|
||||||
|
|
||||||
def pytest_addoption(self, parser):
|
def pytest_addoption(self, parser):
|
||||||
group = parser.addgroup("pylib", "py lib testing options")
|
group = parser.addgroup("pylib", "py lib testing options")
|
||||||
|
|
|
@ -135,6 +135,12 @@ class TestRegistry:
|
||||||
l = list(plugins.listattr('x', reverse=True))
|
l = list(plugins.listattr('x', reverse=True))
|
||||||
assert l == [43, 42, 41]
|
assert l == [43, 42, 41]
|
||||||
|
|
||||||
|
class api4:
|
||||||
|
x = 44
|
||||||
|
l = list(plugins.listattr('x', extra=(api4,)))
|
||||||
|
assert l == range(41, 45)
|
||||||
|
assert len(list(plugins)) == 3 # otherwise extra added
|
||||||
|
|
||||||
def test_api_and_defaults():
|
def test_api_and_defaults():
|
||||||
assert isinstance(py._com.comregistry, Registry)
|
assert isinstance(py._com.comregistry, Registry)
|
||||||
|
|
||||||
|
|
|
@ -1,28 +1,25 @@
|
||||||
|
|
||||||
""" RSync filter test
|
|
||||||
"""
|
|
||||||
|
|
||||||
import py
|
import py
|
||||||
from py.__.test.dist.nodemanage import NodeManager
|
from py.__.test.dist.nodemanage import NodeManager
|
||||||
|
|
||||||
def pytest_funcarg__source(pyfuncitem):
|
class pytest_funcarg__mysetup:
|
||||||
return py.test.ensuretemp(pyfuncitem.getmodpath()).mkdir("source")
|
def __init__(self, request):
|
||||||
def pytest_funcarg__dest(pyfuncitem):
|
basetemp = request.maketempdir()
|
||||||
dest = py.test.ensuretemp(pyfuncitem.getmodpath()).mkdir("dest")
|
basetemp = basetemp.mkdir(request.function.__name__)
|
||||||
return dest
|
self.source = basetemp.mkdir("source")
|
||||||
|
self.dest = basetemp.mkdir("dest")
|
||||||
|
|
||||||
class TestNodeManager:
|
class TestNodeManager:
|
||||||
@py.test.mark.xfail("consider / forbid implicit rsyncdirs?")
|
@py.test.mark.xfail("consider / forbid implicit rsyncdirs?")
|
||||||
def test_rsync_roots_no_roots(self, source, dest):
|
def test_rsync_roots_no_roots(self, mysetup):
|
||||||
source.ensure("dir1", "file1").write("hello")
|
mysetup.source.ensure("dir1", "file1").write("hello")
|
||||||
config = py.test.config._reparse([source])
|
config = py.test.config._reparse([source])
|
||||||
nodemanager = NodeManager(config, ["popen//chdir=%s" % dest])
|
nodemanager = NodeManager(config, ["popen//chdir=%s" % mysetup.dest])
|
||||||
assert nodemanager.config.topdir == source == config.topdir
|
assert nodemanager.config.topdir == source == config.topdir
|
||||||
nodemanager.rsync_roots()
|
nodemanager.rsync_roots()
|
||||||
p, = nodemanager.gwmanager.multi_exec("import os ; channel.send(os.getcwd())").receive_each()
|
p, = nodemanager.gwmanager.multi_exec("import os ; channel.send(os.getcwd())").receive_each()
|
||||||
p = py.path.local(p)
|
p = py.path.local(p)
|
||||||
print "remote curdir", p
|
print "remote curdir", p
|
||||||
assert p == dest.join(config.topdir.basename)
|
assert p == mysetup.dest.join(config.topdir.basename)
|
||||||
assert p.join("dir1").check()
|
assert p.join("dir1").check()
|
||||||
assert p.join("dir1", "file1").check()
|
assert p.join("dir1", "file1").check()
|
||||||
|
|
||||||
|
@ -33,8 +30,9 @@ class TestNodeManager:
|
||||||
nodemanager.setup_nodes([].append)
|
nodemanager.setup_nodes([].append)
|
||||||
nodemanager.wait_nodesready(timeout=2.0)
|
nodemanager.wait_nodesready(timeout=2.0)
|
||||||
|
|
||||||
def test_popen_rsync_subdir(self, testdir, source, dest):
|
def test_popen_rsync_subdir(self, testdir, mysetup):
|
||||||
dir1 = source.mkdir("dir1")
|
source, dest = mysetup.source, mysetup.dest
|
||||||
|
dir1 = mysetup.source.mkdir("dir1")
|
||||||
dir2 = dir1.mkdir("dir2")
|
dir2 = dir1.mkdir("dir2")
|
||||||
dir2.ensure("hello")
|
dir2.ensure("hello")
|
||||||
for rsyncroot in (dir1, source):
|
for rsyncroot in (dir1, source):
|
||||||
|
@ -53,7 +51,8 @@ class TestNodeManager:
|
||||||
assert dest.join("dir1", "dir2", 'hello').check()
|
assert dest.join("dir1", "dir2", 'hello').check()
|
||||||
nodemanager.gwmanager.exit()
|
nodemanager.gwmanager.exit()
|
||||||
|
|
||||||
def test_init_rsync_roots(self, source, dest):
|
def test_init_rsync_roots(self, mysetup):
|
||||||
|
source, dest = mysetup.source, mysetup.dest
|
||||||
dir2 = source.ensure("dir1", "dir2", dir=1)
|
dir2 = source.ensure("dir1", "dir2", dir=1)
|
||||||
source.ensure("dir1", "somefile", dir=1)
|
source.ensure("dir1", "somefile", dir=1)
|
||||||
dir2.ensure("hello")
|
dir2.ensure("hello")
|
||||||
|
@ -68,7 +67,8 @@ class TestNodeManager:
|
||||||
assert not dest.join("dir1").check()
|
assert not dest.join("dir1").check()
|
||||||
assert not dest.join("bogus").check()
|
assert not dest.join("bogus").check()
|
||||||
|
|
||||||
def test_rsyncignore(self, source, dest):
|
def test_rsyncignore(self, mysetup):
|
||||||
|
source, dest = mysetup.source, mysetup.dest
|
||||||
dir2 = source.ensure("dir1", "dir2", dir=1)
|
dir2 = source.ensure("dir1", "dir2", dir=1)
|
||||||
dir5 = source.ensure("dir5", "dir6", "bogus")
|
dir5 = source.ensure("dir5", "dir6", "bogus")
|
||||||
dirf = source.ensure("dir5", "file")
|
dirf = source.ensure("dir5", "file")
|
||||||
|
@ -86,7 +86,8 @@ class TestNodeManager:
|
||||||
assert dest.join("dir5","file").check()
|
assert dest.join("dir5","file").check()
|
||||||
assert not dest.join("dir6").check()
|
assert not dest.join("dir6").check()
|
||||||
|
|
||||||
def test_optimise_popen(self, source, dest):
|
def test_optimise_popen(self, mysetup):
|
||||||
|
source, dest = mysetup.source, mysetup.dest
|
||||||
specs = ["popen"] * 3
|
specs = ["popen"] * 3
|
||||||
source.join("conftest.py").write("rsyncdirs = ['a']")
|
source.join("conftest.py").write("rsyncdirs = ['a']")
|
||||||
source.ensure('a', dir=1)
|
source.ensure('a', dir=1)
|
||||||
|
@ -97,7 +98,8 @@ class TestNodeManager:
|
||||||
assert gwspec._samefilesystem()
|
assert gwspec._samefilesystem()
|
||||||
assert not gwspec.chdir
|
assert not gwspec.chdir
|
||||||
|
|
||||||
def test_setup_DEBUG(self, source, testdir):
|
def test_setup_DEBUG(self, mysetup, testdir):
|
||||||
|
source = mysetup.source
|
||||||
specs = ["popen"] * 2
|
specs = ["popen"] * 2
|
||||||
source.join("conftest.py").write("rsyncdirs = ['a']")
|
source.join("conftest.py").write("rsyncdirs = ['a']")
|
||||||
source.ensure('a', dir=1)
|
source.ensure('a', dir=1)
|
||||||
|
|
|
@ -31,8 +31,8 @@ class EventQueue:
|
||||||
print str(kwargs["excrepr"])
|
print str(kwargs["excrepr"])
|
||||||
|
|
||||||
class MySetup:
|
class MySetup:
|
||||||
def __init__(self, pyfuncitem):
|
def __init__(self, request):
|
||||||
self.pyfuncitem = pyfuncitem
|
self.request = request
|
||||||
|
|
||||||
def geteventargs(self, eventname, timeout=2.0):
|
def geteventargs(self, eventname, timeout=2.0):
|
||||||
eq = EventQueue(self.config.pluginmanager, self.queue)
|
eq = EventQueue(self.config.pluginmanager, self.queue)
|
||||||
|
@ -55,17 +55,11 @@ class MySetup:
|
||||||
print "exiting:", gw
|
print "exiting:", gw
|
||||||
gw.exit()
|
gw.exit()
|
||||||
|
|
||||||
def pytest_funcarg__mysetup(pyfuncitem):
|
def pytest_funcarg__mysetup(request):
|
||||||
mysetup = MySetup(pyfuncitem)
|
mysetup = MySetup(request)
|
||||||
#pyfuncitem.addfinalizer(mysetup.finalize)
|
#pyfuncitem.addfinalizer(mysetup.finalize)
|
||||||
return mysetup
|
return mysetup
|
||||||
|
|
||||||
def pytest_funcarg__testdir(__call__, pyfuncitem):
|
|
||||||
# decorate to make us always change to testdir
|
|
||||||
testdir = __call__.execute(firstresult=True)
|
|
||||||
testdir.chdir()
|
|
||||||
return testdir
|
|
||||||
|
|
||||||
def test_node_hash_equality(mysetup):
|
def test_node_hash_equality(mysetup):
|
||||||
node = mysetup.makenode()
|
node = mysetup.makenode()
|
||||||
node2 = mysetup.makenode()
|
node2 = mysetup.makenode()
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
import py
|
import py
|
||||||
|
|
||||||
class _pytestPlugin:
|
class _pytestPlugin:
|
||||||
def pytest_funcarg___pytest(self, pyfuncitem):
|
def pytest_funcarg___pytest(self, request):
|
||||||
return PytestArg(pyfuncitem)
|
return PytestArg(request)
|
||||||
|
|
||||||
class PytestArg:
|
class PytestArg:
|
||||||
def __init__(self, pyfuncitem):
|
def __init__(self, request):
|
||||||
self.pyfuncitem = pyfuncitem
|
self.request = request
|
||||||
|
|
||||||
def getcallrecorder(self, apiclass, comregistry=None):
|
def getcallrecorder(self, apiclass, comregistry=None):
|
||||||
if comregistry is None:
|
if comregistry is None:
|
||||||
comregistry = self.pyfuncitem.config.pluginmanager.comregistry
|
comregistry = self.request.config.pluginmanager.comregistry
|
||||||
callrecorder = CallRecorder(comregistry)
|
callrecorder = CallRecorder(comregistry)
|
||||||
callrecorder.start_recording(apiclass)
|
callrecorder.start_recording(apiclass)
|
||||||
self.pyfuncitem.addfinalizer(callrecorder.finalize)
|
self.request.addfinalizer(callrecorder.finalize)
|
||||||
return callrecorder
|
return callrecorder
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,6 @@ class DefaultPlugin:
|
||||||
rep = runner.ItemTestReport(item, excinfo, "execute", outerr)
|
rep = runner.ItemTestReport(item, excinfo, "execute", outerr)
|
||||||
item.config.api.pytest_itemtestreport(rep=rep)
|
item.config.api.pytest_itemtestreport(rep=rep)
|
||||||
|
|
||||||
# XXX make this access pyfuncitem.args or funcargs
|
|
||||||
def pytest_pyfunc_call(self, pyfuncitem, args, kwargs):
|
def pytest_pyfunc_call(self, pyfuncitem, args, kwargs):
|
||||||
pyfuncitem.obj(*args, **kwargs)
|
pyfuncitem.obj(*args, **kwargs)
|
||||||
|
|
||||||
|
|
|
@ -2,14 +2,14 @@ import py
|
||||||
|
|
||||||
class IocapturePlugin:
|
class IocapturePlugin:
|
||||||
""" capture sys.stdout/sys.stderr / fd1/fd2. """
|
""" capture sys.stdout/sys.stderr / fd1/fd2. """
|
||||||
def pytest_funcarg__stdcapture(self, pyfuncitem):
|
def pytest_funcarg__stdcapture(self, request):
|
||||||
capture = Capture(py.io.StdCapture)
|
capture = Capture(py.io.StdCapture)
|
||||||
pyfuncitem.addfinalizer(capture.finalize)
|
request.addfinalizer(capture.finalize)
|
||||||
return capture
|
return capture
|
||||||
|
|
||||||
def pytest_funcarg__stdcapturefd(self, pyfuncitem):
|
def pytest_funcarg__stdcapturefd(self, request):
|
||||||
capture = Capture(py.io.StdCaptureFD)
|
capture = Capture(py.io.StdCaptureFD)
|
||||||
pyfuncitem.addfinalizer(capture.finalize)
|
request.addfinalizer(capture.finalize)
|
||||||
return capture
|
return capture
|
||||||
|
|
||||||
class Capture:
|
class Capture:
|
||||||
|
|
|
@ -2,9 +2,9 @@ import os
|
||||||
|
|
||||||
class MonkeypatchPlugin:
|
class MonkeypatchPlugin:
|
||||||
""" setattr-monkeypatching with automatical reversal after test. """
|
""" setattr-monkeypatching with automatical reversal after test. """
|
||||||
def pytest_funcarg__monkeypatch(self, pyfuncitem):
|
def pytest_funcarg__monkeypatch(self, request):
|
||||||
monkeypatch = MonkeyPatch()
|
monkeypatch = MonkeyPatch()
|
||||||
pyfuncitem.addfinalizer(monkeypatch.finalize)
|
request.addfinalizer(monkeypatch.finalize)
|
||||||
return monkeypatch
|
return monkeypatch
|
||||||
|
|
||||||
notset = object()
|
notset = object()
|
||||||
|
|
|
@ -6,37 +6,23 @@ from py.__.test.plugin import api
|
||||||
|
|
||||||
class PlugintesterPlugin:
|
class PlugintesterPlugin:
|
||||||
""" test support code for testing pytest plugins. """
|
""" test support code for testing pytest plugins. """
|
||||||
def pytest_funcarg__plugintester(self, pyfuncitem):
|
def pytest_funcarg__plugintester(self, request):
|
||||||
pt = PluginTester(pyfuncitem)
|
return PluginTester(request)
|
||||||
pyfuncitem.addfinalizer(pt.finalize)
|
|
||||||
return pt
|
|
||||||
|
|
||||||
class Support(object):
|
class PluginTester:
|
||||||
def __init__(self, pyfuncitem):
|
def __init__(self, request):
|
||||||
""" instantiated per function that requests it. """
|
self.request = request
|
||||||
self.pyfuncitem = pyfuncitem
|
|
||||||
|
|
||||||
def getmoditem(self):
|
|
||||||
for colitem in self.pyfuncitem.listchain():
|
|
||||||
if isinstance(colitem, colitem.Module):
|
|
||||||
return colitem
|
|
||||||
|
|
||||||
def finalize(self):
|
|
||||||
""" called after test function finished execution"""
|
|
||||||
|
|
||||||
class PluginTester(Support):
|
|
||||||
def testdir(self):
|
def testdir(self):
|
||||||
# XXX import differently, eg.
|
|
||||||
# FSTester = self.pyfuncitem.config.pluginmanager.getpluginattr("pytester", "FSTester")
|
|
||||||
from pytest_pytester import TmpTestdir
|
from pytest_pytester import TmpTestdir
|
||||||
crunner = TmpTestdir(self.pyfuncitem)
|
crunner = TmpTestdir(self.request)
|
||||||
self.pyfuncitem.addfinalizer(crunner.finalize)
|
self.request.addfinalizer(crunner.finalize)
|
||||||
#
|
#
|
||||||
for colitem in self.pyfuncitem.listchain():
|
#for colitem in self.request.listchain():
|
||||||
if isinstance(colitem, py.test.collect.Module) and \
|
# if isinstance(colitem, py.test.collect.Module) and \
|
||||||
colitem.name.startswith("pytest_"):
|
# colitem.name.startswith("pytest_"):
|
||||||
crunner.plugins.append(colitem.fspath.purebasename)
|
# crunner.plugins.append(colitem.fspath.purebasename)
|
||||||
break
|
# break
|
||||||
return crunner
|
return crunner
|
||||||
|
|
||||||
def apicheck(self, pluginclass):
|
def apicheck(self, pluginclass):
|
||||||
|
|
|
@ -11,19 +11,19 @@ import api
|
||||||
|
|
||||||
|
|
||||||
class PytesterPlugin:
|
class PytesterPlugin:
|
||||||
def pytest_funcarg__linecomp(self, pyfuncitem):
|
def pytest_funcarg__linecomp(self, request):
|
||||||
return LineComp()
|
return LineComp()
|
||||||
|
|
||||||
def pytest_funcarg__LineMatcher(self, pyfuncitem):
|
def pytest_funcarg__LineMatcher(self, request):
|
||||||
return LineMatcher
|
return LineMatcher
|
||||||
|
|
||||||
def pytest_funcarg__testdir(self, pyfuncitem):
|
def pytest_funcarg__testdir(self, request):
|
||||||
tmptestdir = TmpTestdir(pyfuncitem)
|
tmptestdir = TmpTestdir(request)
|
||||||
return tmptestdir
|
return tmptestdir
|
||||||
|
|
||||||
def pytest_funcarg__eventrecorder(self, pyfuncitem):
|
def pytest_funcarg__eventrecorder(self, request):
|
||||||
evrec = EventRecorder(py._com.comregistry)
|
evrec = EventRecorder(py._com.comregistry)
|
||||||
pyfuncitem.addfinalizer(lambda: evrec.comregistry.unregister(evrec))
|
request.addfinalizer(lambda: evrec.comregistry.unregister(evrec))
|
||||||
return evrec
|
return evrec
|
||||||
|
|
||||||
def test_generic(plugintester):
|
def test_generic(plugintester):
|
||||||
|
@ -38,11 +38,11 @@ class RunResult:
|
||||||
self.stderr = LineMatcher(errlines)
|
self.stderr = LineMatcher(errlines)
|
||||||
|
|
||||||
class TmpTestdir:
|
class TmpTestdir:
|
||||||
def __init__(self, pyfuncitem):
|
def __init__(self, request):
|
||||||
self.pyfuncitem = pyfuncitem
|
self.request = request
|
||||||
# XXX remove duplication with tmpdir plugin
|
# XXX remove duplication with tmpdir plugin
|
||||||
basetmp = pyfuncitem.config.ensuretemp("testdir")
|
basetmp = request.config.ensuretemp("testdir")
|
||||||
name = pyfuncitem.name
|
name = request.function.__name__
|
||||||
for i in range(100):
|
for i in range(100):
|
||||||
try:
|
try:
|
||||||
tmpdir = basetmp.mkdir(name + str(i))
|
tmpdir = basetmp.mkdir(name + str(i))
|
||||||
|
@ -57,7 +57,7 @@ class TmpTestdir:
|
||||||
self._syspathremove = []
|
self._syspathremove = []
|
||||||
self.chdir() # always chdir
|
self.chdir() # always chdir
|
||||||
assert hasattr(self, '_olddir')
|
assert hasattr(self, '_olddir')
|
||||||
self.pyfuncitem.addfinalizer(self.finalize)
|
self.request.addfinalizer(self.finalize)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<TmpTestdir %r>" % (self.tmpdir,)
|
return "<TmpTestdir %r>" % (self.tmpdir,)
|
||||||
|
@ -78,7 +78,7 @@ class TmpTestdir:
|
||||||
sorter.callrecorder = CallRecorder(registry)
|
sorter.callrecorder = CallRecorder(registry)
|
||||||
sorter.callrecorder.start_recording(api.PluginHooks)
|
sorter.callrecorder.start_recording(api.PluginHooks)
|
||||||
sorter.api = sorter.callrecorder.api
|
sorter.api = sorter.callrecorder.api
|
||||||
self.pyfuncitem.addfinalizer(sorter.callrecorder.finalize)
|
self.request.addfinalizer(sorter.callrecorder.finalize)
|
||||||
return sorter
|
return sorter
|
||||||
|
|
||||||
def chdir(self):
|
def chdir(self):
|
||||||
|
@ -90,7 +90,7 @@ class TmpTestdir:
|
||||||
items = kwargs.items()
|
items = kwargs.items()
|
||||||
if args:
|
if args:
|
||||||
source = "\n".join(map(str, args))
|
source = "\n".join(map(str, args))
|
||||||
basename = self.pyfuncitem.name
|
basename = self.request.function.__name__
|
||||||
items.insert(0, (basename, source))
|
items.insert(0, (basename, source))
|
||||||
ret = None
|
ret = None
|
||||||
for name, value in items:
|
for name, value in items:
|
||||||
|
@ -139,7 +139,7 @@ class TmpTestdir:
|
||||||
# used from runner functional tests
|
# used from runner functional tests
|
||||||
item = self.getitem(source)
|
item = self.getitem(source)
|
||||||
# the test class where we are called from wants to provide the runner
|
# the test class where we are called from wants to provide the runner
|
||||||
testclassinstance = self.pyfuncitem.obj.im_self
|
testclassinstance = self.request.function.im_self
|
||||||
runner = testclassinstance.getrunner()
|
runner = testclassinstance.getrunner()
|
||||||
return runner(item, **runnerargs)
|
return runner(item, **runnerargs)
|
||||||
|
|
||||||
|
@ -200,7 +200,7 @@ class TmpTestdir:
|
||||||
return self.config.getfsnode(path)
|
return self.config.getfsnode(path)
|
||||||
|
|
||||||
def getmodulecol(self, source, configargs=(), withinit=False):
|
def getmodulecol(self, source, configargs=(), withinit=False):
|
||||||
kw = {self.pyfuncitem.name: py.code.Source(source).strip()}
|
kw = {self.request.function.__name__: py.code.Source(source).strip()}
|
||||||
path = self.makepyfile(**kw)
|
path = self.makepyfile(**kw)
|
||||||
if withinit:
|
if withinit:
|
||||||
self.makepyfile(__init__ = "#")
|
self.makepyfile(__init__ = "#")
|
||||||
|
@ -455,3 +455,10 @@ class LineMatcher:
|
||||||
return extralines
|
return extralines
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def test_parseconfig(testdir):
|
||||||
|
config1 = testdir.parseconfig()
|
||||||
|
config2 = testdir.parseconfig()
|
||||||
|
assert config2 != config1
|
||||||
|
assert config1 != py.test.config
|
||||||
|
|
|
@ -382,10 +382,15 @@ class TestApigenLinkRole:
|
||||||
"resolve_linkrole('source', 'py/foo/bar.py')")
|
"resolve_linkrole('source', 'py/foo/bar.py')")
|
||||||
|
|
||||||
|
|
||||||
def pytest_funcarg__testdir(__call__, pyfuncitem):
|
def pytest_funcarg__testdir(request):
|
||||||
testdir = __call__.execute(firstresult=True)
|
testdir = request.call_next_provider()
|
||||||
testdir.makepyfile(confrest="from py.__.misc.rest import Project")
|
testdir.makepyfile(confrest="from py.__.misc.rest import Project")
|
||||||
testdir.plugins.append(RestdocPlugin())
|
testdir.plugins.append(RestdocPlugin())
|
||||||
|
count = 0
|
||||||
|
for p in testdir.plugins:
|
||||||
|
if isinstance(p, RestdocPlugin):
|
||||||
|
count += 1
|
||||||
|
assert count < 2
|
||||||
return testdir
|
return testdir
|
||||||
|
|
||||||
class TestDoctest:
|
class TestDoctest:
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import uuid
|
|
||||||
import py
|
import py
|
||||||
from pytest_resultlog import ResultLog
|
from pytest_resultlog import ResultLog
|
||||||
|
|
||||||
|
@ -65,7 +64,7 @@ class JSONResultArchive(object):
|
||||||
self._flush()
|
self._flush()
|
||||||
|
|
||||||
def append_data(self, data):
|
def append_data(self, data):
|
||||||
runid = uuid.uuid4()
|
runid = py.std.uuid.uuid4()
|
||||||
for item in data:
|
for item in data:
|
||||||
item = item.copy()
|
item = item.copy()
|
||||||
item['runid'] = str(runid)
|
item['runid'] = str(runid)
|
||||||
|
@ -100,7 +99,7 @@ class SQLiteResultArchive(object):
|
||||||
|
|
||||||
def append_data(self, data):
|
def append_data(self, data):
|
||||||
flat_data = []
|
flat_data = []
|
||||||
runid = uuid.uuid4()
|
runid = py.std.uuid.uuid4()
|
||||||
for item in data:
|
for item in data:
|
||||||
item = item.copy()
|
item = item.copy()
|
||||||
item['runid'] = str(runid)
|
item['runid'] = str(runid)
|
||||||
|
|
|
@ -157,6 +157,7 @@ class TestWithFunctionIntegration:
|
||||||
# ignorant regarding formatting details.
|
# ignorant regarding formatting details.
|
||||||
def getresultlog(self, testdir, arg):
|
def getresultlog(self, testdir, arg):
|
||||||
resultlog = testdir.tmpdir.join("resultlog")
|
resultlog = testdir.tmpdir.join("resultlog")
|
||||||
|
testdir.plugins.append("resultlog")
|
||||||
args = ["--resultlog=%s" % resultlog] + [arg]
|
args = ["--resultlog=%s" % resultlog] + [arg]
|
||||||
testdir.runpytest(*args)
|
testdir.runpytest(*args)
|
||||||
return filter(None, resultlog.readlines(cr=0))
|
return filter(None, resultlog.readlines(cr=0))
|
||||||
|
@ -166,7 +167,6 @@ class TestWithFunctionIntegration:
|
||||||
ok = testdir.makepyfile(test_collection_ok="")
|
ok = testdir.makepyfile(test_collection_ok="")
|
||||||
skip = testdir.makepyfile(test_collection_skip="import py ; py.test.skip('hello')")
|
skip = testdir.makepyfile(test_collection_skip="import py ; py.test.skip('hello')")
|
||||||
fail = testdir.makepyfile(test_collection_fail="XXX")
|
fail = testdir.makepyfile(test_collection_fail="XXX")
|
||||||
|
|
||||||
lines = self.getresultlog(testdir, ok)
|
lines = self.getresultlog(testdir, ok)
|
||||||
assert not lines
|
assert not lines
|
||||||
|
|
||||||
|
@ -226,6 +226,7 @@ class TestWithFunctionIntegration:
|
||||||
def test_generic(plugintester, LineMatcher):
|
def test_generic(plugintester, LineMatcher):
|
||||||
plugintester.apicheck(ResultlogPlugin)
|
plugintester.apicheck(ResultlogPlugin)
|
||||||
testdir = plugintester.testdir()
|
testdir = plugintester.testdir()
|
||||||
|
testdir.plugins.append("resultlog")
|
||||||
testdir.makepyfile("""
|
testdir.makepyfile("""
|
||||||
import py
|
import py
|
||||||
def test_pass():
|
def test_pass():
|
||||||
|
|
|
@ -13,9 +13,9 @@ class TmpdirPlugin:
|
||||||
""" provide temporary directories to test functions and methods.
|
""" provide temporary directories to test functions and methods.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def pytest_funcarg__tmpdir(self, pyfuncitem):
|
def pytest_funcarg__tmpdir(self, request):
|
||||||
name = pyfuncitem.name
|
name = request.function.__name__
|
||||||
return pyfuncitem.config.mktemp(name, numbered=True)
|
return request.config.mktemp(name, numbered=True)
|
||||||
|
|
||||||
# ===============================================================================
|
# ===============================================================================
|
||||||
#
|
#
|
||||||
|
@ -29,7 +29,7 @@ def test_generic(plugintester):
|
||||||
def test_funcarg(testdir):
|
def test_funcarg(testdir):
|
||||||
item = testdir.getitem("def test_func(tmpdir): pass")
|
item = testdir.getitem("def test_func(tmpdir): pass")
|
||||||
plugin = TmpdirPlugin()
|
plugin = TmpdirPlugin()
|
||||||
p = plugin.pytest_funcarg__tmpdir(item)
|
p = plugin.pytest_funcarg__tmpdir(item.getrequest("tmpdir"))
|
||||||
assert p.check()
|
assert p.check()
|
||||||
bn = p.basename.strip("0123456789-")
|
bn = p.basename.strip("0123456789-")
|
||||||
assert bn.endswith("test_func")
|
assert bn.endswith("test_func")
|
||||||
|
|
|
@ -18,6 +18,7 @@ class PluginManager(object):
|
||||||
|
|
||||||
def register(self, plugin):
|
def register(self, plugin):
|
||||||
self.api.pytest_plugin_registered(plugin=plugin)
|
self.api.pytest_plugin_registered(plugin=plugin)
|
||||||
|
import types
|
||||||
self.comregistry.register(plugin)
|
self.comregistry.register(plugin)
|
||||||
|
|
||||||
def unregister(self, plugin):
|
def unregister(self, plugin):
|
||||||
|
@ -79,8 +80,8 @@ class PluginManager(object):
|
||||||
for x in self.comregistry.listattr(attrname):
|
for x in self.comregistry.listattr(attrname):
|
||||||
return x
|
return x
|
||||||
|
|
||||||
def listattr(self, attrname, plugins=None):
|
def listattr(self, attrname, plugins=None, extra=()):
|
||||||
return self.comregistry.listattr(attrname, plugins=plugins)
|
return self.comregistry.listattr(attrname, plugins=plugins, extra=extra)
|
||||||
|
|
||||||
def call_firstresult(self, *args, **kwargs):
|
def call_firstresult(self, *args, **kwargs):
|
||||||
return self.comregistry.call_firstresult(*args, **kwargs)
|
return self.comregistry.call_firstresult(*args, **kwargs)
|
||||||
|
|
|
@ -177,7 +177,7 @@ class Module(py.test.collect.File, PyCollectorMixin):
|
||||||
#print "*" * 20, "INVOKE assertion", self
|
#print "*" * 20, "INVOKE assertion", self
|
||||||
py.magic.invoke(assertion=1)
|
py.magic.invoke(assertion=1)
|
||||||
mod = self.obj
|
mod = self.obj
|
||||||
self.config.pluginmanager.register(mod)
|
#self.config.pluginmanager.register(mod)
|
||||||
if hasattr(mod, 'setup_module'):
|
if hasattr(mod, 'setup_module'):
|
||||||
self.obj.setup_module(mod)
|
self.obj.setup_module(mod)
|
||||||
|
|
||||||
|
@ -187,7 +187,7 @@ class Module(py.test.collect.File, PyCollectorMixin):
|
||||||
if not self.config.option.nomagic:
|
if not self.config.option.nomagic:
|
||||||
#print "*" * 20, "revoke assertion", self
|
#print "*" * 20, "revoke assertion", self
|
||||||
py.magic.revoke(assertion=1)
|
py.magic.revoke(assertion=1)
|
||||||
self.config.pluginmanager.unregister(self.obj)
|
#self.config.pluginmanager.unregister(self.obj)
|
||||||
|
|
||||||
class Class(PyCollectorMixin, py.test.collect.Collector):
|
class Class(PyCollectorMixin, py.test.collect.Collector):
|
||||||
|
|
||||||
|
@ -365,38 +365,15 @@ class Function(FunctionMixin, py.test.collect.Item):
|
||||||
for i, argname in py.builtin.enumerate(argnames):
|
for i, argname in py.builtin.enumerate(argnames):
|
||||||
if i < startindex:
|
if i < startindex:
|
||||||
continue
|
continue
|
||||||
|
request = self.getrequest(argname)
|
||||||
try:
|
try:
|
||||||
self.funcargs[argname] = self.lookup_onearg(argname)
|
self.funcargs[argname] = request.call_next_provider()
|
||||||
except LookupError, e:
|
except request.Error:
|
||||||
numdefaults = len(funcobj.func_defaults or ())
|
numdefaults = len(funcobj.func_defaults or ())
|
||||||
if i + numdefaults >= len(argnames):
|
if i + numdefaults >= len(argnames):
|
||||||
continue # continue # seems that our args have defaults
|
continue # our args have defaults XXX issue warning?
|
||||||
else:
|
else:
|
||||||
raise
|
request._raiselookupfailed()
|
||||||
|
|
||||||
def lookup_onearg(self, argname):
|
|
||||||
prefix = "pytest_funcarg__"
|
|
||||||
#makerlist = self.config.pluginmanager.listattr(prefix + argname)
|
|
||||||
value = self.config.pluginmanager.call_firstresult(prefix + argname, pyfuncitem=self)
|
|
||||||
if value is not None:
|
|
||||||
return value
|
|
||||||
else:
|
|
||||||
self._raisefuncargerror(argname, prefix)
|
|
||||||
|
|
||||||
def _raisefuncargerror(self, argname, prefix="pytest_funcarg__"):
|
|
||||||
metainfo = self.repr_metainfo()
|
|
||||||
available = []
|
|
||||||
plugins = list(self.config.pluginmanager.comregistry)
|
|
||||||
#plugins.extend(self.config.pluginmanager.registry.plugins)
|
|
||||||
for plugin in plugins:
|
|
||||||
for name in vars(plugin.__class__):
|
|
||||||
if name.startswith(prefix):
|
|
||||||
name = name[len(prefix):]
|
|
||||||
if name not in available:
|
|
||||||
available.append(name)
|
|
||||||
msg = "funcargument %r not found for: %s" %(argname,metainfo.verboseline())
|
|
||||||
msg += "\n available funcargs: %s" %(", ".join(available),)
|
|
||||||
raise LookupError(msg)
|
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
try:
|
try:
|
||||||
|
@ -410,6 +387,70 @@ class Function(FunctionMixin, py.test.collect.Item):
|
||||||
def __ne__(self, other):
|
def __ne__(self, other):
|
||||||
return not self == other
|
return not self == other
|
||||||
|
|
||||||
|
def getrequest(self, argname):
|
||||||
|
return FuncargRequest(pyfuncitem=self, argname=argname)
|
||||||
|
|
||||||
|
|
||||||
|
class FuncargRequest:
|
||||||
|
_argprefix = "pytest_funcarg__"
|
||||||
|
|
||||||
|
class Error(LookupError):
|
||||||
|
""" error on performing funcarg request. """
|
||||||
|
|
||||||
|
def __init__(self, pyfuncitem, argname):
|
||||||
|
# XXX make pyfuncitem _pyfuncitem
|
||||||
|
self._pyfuncitem = pyfuncitem
|
||||||
|
self.argname = argname
|
||||||
|
self.function = pyfuncitem.obj
|
||||||
|
self.config = pyfuncitem.config
|
||||||
|
self.fspath = pyfuncitem.fspath
|
||||||
|
self._plugins = self._getplugins()
|
||||||
|
self._methods = self.config.pluginmanager.listattr(
|
||||||
|
plugins=self._plugins,
|
||||||
|
attrname=self._argprefix + str(argname)
|
||||||
|
)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<FuncargRequest %r for %r>" %(self.argname, self._pyfuncitem)
|
||||||
|
|
||||||
|
|
||||||
|
def _getplugins(self):
|
||||||
|
plugins = []
|
||||||
|
current = self._pyfuncitem
|
||||||
|
while not isinstance(current, Module):
|
||||||
|
current = current.parent
|
||||||
|
if isinstance(current, (Instance, Module)):
|
||||||
|
plugins.insert(0, current.obj)
|
||||||
|
return self.config.pluginmanager.getplugins() + plugins
|
||||||
|
|
||||||
|
def call_next_provider(self):
|
||||||
|
if not self._methods:
|
||||||
|
raise self.Error("no provider methods left")
|
||||||
|
nextmethod = self._methods.pop()
|
||||||
|
return nextmethod(request=self)
|
||||||
|
|
||||||
|
def addfinalizer(self, finalizer):
|
||||||
|
self._pyfuncitem.addfinalizer(finalizer)
|
||||||
|
|
||||||
|
def maketempdir(self):
|
||||||
|
basetemp = self.config.getbasetemp()
|
||||||
|
tmp = py.path.local.make_numbered_dir(
|
||||||
|
prefix=self.function.__name__ + "_",
|
||||||
|
keep=0, rootdir=basetemp)
|
||||||
|
return tmp
|
||||||
|
|
||||||
|
def _raiselookupfailed(self):
|
||||||
|
available = []
|
||||||
|
for plugin in self._plugins:
|
||||||
|
for name in vars(plugin.__class__):
|
||||||
|
if name.startswith(self._argprefix):
|
||||||
|
name = name[len(self._argprefix):]
|
||||||
|
if name not in available:
|
||||||
|
available.append(name)
|
||||||
|
metainfo = self._pyfuncitem.repr_metainfo()
|
||||||
|
msg = "funcargument %r not found for: %s" %(self.argname,metainfo.verboseline())
|
||||||
|
msg += "\n available funcargs: %s" %(", ".join(available),)
|
||||||
|
raise LookupError(msg)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# DEPRECATED
|
|
||||||
#from py.__.test.plugin.pytest_doctest import DoctestFile
|
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
|
|
||||||
pytest_plugins = "pytest_xfail", "pytest_pytester", "pytest_tmpdir"
|
pytest_plugins = "pytest_xfail", "pytest_pytester", "pytest_tmpdir"
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,130 @@
|
||||||
|
import py
|
||||||
|
|
||||||
|
class TestFuncargs:
|
||||||
|
def test_funcarg_lookupfails(self, testdir):
|
||||||
|
testdir.makeconftest("""
|
||||||
|
class ConftestPlugin:
|
||||||
|
def pytest_funcarg__xyzsomething(self, request):
|
||||||
|
return 42
|
||||||
|
""")
|
||||||
|
item = testdir.getitem("def test_func(some): pass")
|
||||||
|
exc = py.test.raises(LookupError, "item.setupargs()")
|
||||||
|
s = str(exc.value)
|
||||||
|
assert s.find("xyzsomething") != -1
|
||||||
|
|
||||||
|
def test_funcarg_lookup_default(self, testdir):
|
||||||
|
item = testdir.getitem("def test_func(some, other=42): pass")
|
||||||
|
class Provider:
|
||||||
|
def pytest_funcarg__some(self, request):
|
||||||
|
return request.function.__name__
|
||||||
|
item.config.pluginmanager.register(Provider())
|
||||||
|
item.setupargs()
|
||||||
|
assert len(item.funcargs) == 1
|
||||||
|
|
||||||
|
def test_funcarg_lookup_default_gets_overriden(self, testdir):
|
||||||
|
item = testdir.getitem("def test_func(some=42, other=13): pass")
|
||||||
|
class Provider:
|
||||||
|
def pytest_funcarg__other(self, request):
|
||||||
|
return request.function.__name__
|
||||||
|
item.config.pluginmanager.register(Provider())
|
||||||
|
item.setupargs()
|
||||||
|
assert len(item.funcargs) == 1
|
||||||
|
name, value = item.funcargs.popitem()
|
||||||
|
assert name == "other"
|
||||||
|
assert value == item.name
|
||||||
|
|
||||||
|
def test_funcarg_basic(self, testdir):
|
||||||
|
item = testdir.getitem("def test_func(some, other): pass")
|
||||||
|
class Provider:
|
||||||
|
def pytest_funcarg__some(self, request):
|
||||||
|
return request.function.__name__
|
||||||
|
def pytest_funcarg__other(self, request):
|
||||||
|
return 42
|
||||||
|
item.config.pluginmanager.register(Provider())
|
||||||
|
item.setupargs()
|
||||||
|
assert len(item.funcargs) == 2
|
||||||
|
assert item.funcargs['some'] == "test_func"
|
||||||
|
assert item.funcargs['other'] == 42
|
||||||
|
|
||||||
|
def test_funcarg_lookup_modulelevel(self, testdir):
|
||||||
|
modcol = testdir.getmodulecol("""
|
||||||
|
def pytest_funcarg__something(request):
|
||||||
|
return request.function.__name__
|
||||||
|
|
||||||
|
class TestClass:
|
||||||
|
def test_method(self, something):
|
||||||
|
pass
|
||||||
|
def test_func(something):
|
||||||
|
pass
|
||||||
|
""")
|
||||||
|
item1, item2 = testdir.genitems([modcol])
|
||||||
|
item1.setupargs()
|
||||||
|
assert item1.funcargs['something'] == "test_method"
|
||||||
|
item2.setupargs()
|
||||||
|
assert item2.funcargs['something'] == "test_func"
|
||||||
|
|
||||||
|
class TestRequest:
|
||||||
|
def test_request_attributes(self, testdir):
|
||||||
|
item = testdir.getitem("""
|
||||||
|
def pytest_funcarg__something(request): pass
|
||||||
|
def test_func(something): pass
|
||||||
|
""")
|
||||||
|
req = item.getrequest("other")
|
||||||
|
assert req.argname == "other"
|
||||||
|
assert req.function == item.obj
|
||||||
|
assert req.function.__name__ == "test_func"
|
||||||
|
assert req.config == item.config
|
||||||
|
assert repr(req).find(req.function.__name__) != -1
|
||||||
|
|
||||||
|
def test_request_contains_funcargs_methods(self, testdir):
|
||||||
|
modcol = testdir.getmodulecol("""
|
||||||
|
def pytest_funcarg__something(request):
|
||||||
|
pass
|
||||||
|
class TestClass:
|
||||||
|
def pytest_funcarg__something(self, request):
|
||||||
|
pass
|
||||||
|
def test_method(self, something):
|
||||||
|
pass
|
||||||
|
""")
|
||||||
|
item1, = testdir.genitems([modcol])
|
||||||
|
assert item1.name == "test_method"
|
||||||
|
methods = item1.getrequest("something")._methods
|
||||||
|
assert len(methods) == 2
|
||||||
|
method1, method2 = methods
|
||||||
|
assert not hasattr(method1, 'im_self')
|
||||||
|
assert method2.im_self is not None
|
||||||
|
|
||||||
|
def test_request_call_next_provider(self, testdir):
|
||||||
|
item = testdir.getitem("""
|
||||||
|
def pytest_funcarg__something(request): pass
|
||||||
|
def test_func(something): pass
|
||||||
|
""")
|
||||||
|
req = item.getrequest("something")
|
||||||
|
val = req.call_next_provider()
|
||||||
|
assert val is None
|
||||||
|
py.test.raises(req.Error, "req.call_next_provider()")
|
||||||
|
|
||||||
|
def test_request_addfinalizer(self, testdir):
|
||||||
|
item = testdir.getitem("""
|
||||||
|
def pytest_funcarg__something(request): pass
|
||||||
|
def test_func(something): pass
|
||||||
|
""")
|
||||||
|
req = item.getrequest("something")
|
||||||
|
l = [1]
|
||||||
|
req.addfinalizer(l.pop)
|
||||||
|
item.teardown()
|
||||||
|
|
||||||
|
def test_request_maketemp(self, testdir):
|
||||||
|
item = testdir.getitem("def test_func(): pass")
|
||||||
|
req = item.getrequest("xxx")
|
||||||
|
tmpdir = req.maketempdir()
|
||||||
|
tmpdir2 = req.maketempdir()
|
||||||
|
assert tmpdir != tmpdir2
|
||||||
|
assert tmpdir.basename.startswith("test_func")
|
||||||
|
assert tmpdir2.basename.startswith("test_func")
|
||||||
|
|
||||||
|
def test_request_getmodulepath(self, testdir):
|
||||||
|
modcol = testdir.getmodulecol("def test_somefunc(): pass")
|
||||||
|
item, = testdir.genitems([modcol])
|
||||||
|
req = item.getrequest("hello")
|
||||||
|
assert req.fspath == modcol.fspath
|
|
@ -1,31 +1,27 @@
|
||||||
import py
|
import py
|
||||||
|
|
||||||
def pytest_funcarg__pickletransport(pyfuncitem):
|
def setglobals(request):
|
||||||
return ImmutablePickleTransport()
|
|
||||||
|
|
||||||
def pytest_pyfunc_call(__call__, pyfuncitem, args, kwargs):
|
|
||||||
# for each function call we patch py._com.comregistry
|
|
||||||
# so that the unpickling of config objects
|
|
||||||
# (which bind to this mechanism) doesn't do harm
|
|
||||||
# usually config objects are no meant to be unpickled in
|
|
||||||
# the same system
|
|
||||||
oldconfig = py.test.config
|
oldconfig = py.test.config
|
||||||
oldcom = py._com.comregistry
|
oldcom = py._com.comregistry
|
||||||
print "setting py.test.config to None"
|
print "setting py.test.config to None"
|
||||||
py.test.config = None
|
py.test.config = None
|
||||||
py._com.comregistry = py._com.Registry()
|
py._com.comregistry = py._com.Registry()
|
||||||
try:
|
def resetglobals():
|
||||||
return __call__.execute(firstresult=True)
|
|
||||||
finally:
|
|
||||||
print "setting py.test.config to", oldconfig
|
print "setting py.test.config to", oldconfig
|
||||||
py.test.config = oldconfig
|
py.test.config = oldconfig
|
||||||
py._com.comregistry = oldcom
|
py._com.comregistry = oldcom
|
||||||
|
request.addfinalizer(resetglobals)
|
||||||
|
|
||||||
|
def pytest_funcarg__testdir(request):
|
||||||
|
setglobals(request)
|
||||||
|
return request.call_next_provider()
|
||||||
|
|
||||||
class ImmutablePickleTransport:
|
class ImmutablePickleTransport:
|
||||||
def __init__(self):
|
def __init__(self, request):
|
||||||
from py.__.test.dist.mypickle import ImmutablePickler
|
from py.__.test.dist.mypickle import ImmutablePickler
|
||||||
self.p1 = ImmutablePickler(uneven=0)
|
self.p1 = ImmutablePickler(uneven=0)
|
||||||
self.p2 = ImmutablePickler(uneven=1)
|
self.p2 = ImmutablePickler(uneven=1)
|
||||||
|
setglobals(request)
|
||||||
|
|
||||||
def p1_to_p2(self, obj):
|
def p1_to_p2(self, obj):
|
||||||
return self.p2.loads(self.p1.dumps(obj))
|
return self.p2.loads(self.p1.dumps(obj))
|
||||||
|
@ -39,6 +35,8 @@ class ImmutablePickleTransport:
|
||||||
return p2config
|
return p2config
|
||||||
|
|
||||||
class TestImmutablePickling:
|
class TestImmutablePickling:
|
||||||
|
pytest_funcarg__pickletransport = ImmutablePickleTransport
|
||||||
|
|
||||||
def test_pickle_config(self, testdir, pickletransport):
|
def test_pickle_config(self, testdir, pickletransport):
|
||||||
config1 = testdir.parseconfig()
|
config1 = testdir.parseconfig()
|
||||||
assert config1.topdir == testdir.tmpdir
|
assert config1.topdir == testdir.tmpdir
|
||||||
|
|
|
@ -34,13 +34,6 @@ class TestModule:
|
||||||
x = l.pop()
|
x = l.pop()
|
||||||
assert x is None
|
assert x is None
|
||||||
|
|
||||||
def test_module_participates_as_plugin(self, testdir):
|
|
||||||
modcol = testdir.getmodulecol("")
|
|
||||||
modcol.setup()
|
|
||||||
assert modcol.config.pluginmanager.isregistered(modcol.obj)
|
|
||||||
modcol.teardown()
|
|
||||||
assert not modcol.config.pluginmanager.isregistered(modcol.obj)
|
|
||||||
|
|
||||||
def test_module_considers_pluginmanager_at_import(self, testdir):
|
def test_module_considers_pluginmanager_at_import(self, testdir):
|
||||||
modcol = testdir.getmodulecol("pytest_plugins='xasdlkj',")
|
modcol = testdir.getmodulecol("pytest_plugins='xasdlkj',")
|
||||||
py.test.raises(ImportError, "modcol.obj")
|
py.test.raises(ImportError, "modcol.obj")
|
||||||
|
@ -243,88 +236,6 @@ class TestFunction:
|
||||||
assert f1 == f1_b
|
assert f1 == f1_b
|
||||||
assert not f1 != f1_b
|
assert not f1 != f1_b
|
||||||
|
|
||||||
def test_funcarg_lookupfails(self, testdir):
|
|
||||||
testdir.makeconftest("""
|
|
||||||
class ConftestPlugin:
|
|
||||||
def pytest_funcarg__something(self, pyfuncitem):
|
|
||||||
return 42
|
|
||||||
""")
|
|
||||||
item = testdir.getitem("def test_func(some): pass")
|
|
||||||
exc = py.test.raises(LookupError, "item.setupargs()")
|
|
||||||
s = str(exc.value)
|
|
||||||
assert s.find("something") != -1
|
|
||||||
|
|
||||||
def test_funcarg_lookup_default(self, testdir):
|
|
||||||
item = testdir.getitem("def test_func(some, other=42): pass")
|
|
||||||
class Provider:
|
|
||||||
def pytest_funcarg__some(self, pyfuncitem):
|
|
||||||
return pyfuncitem.name
|
|
||||||
item.config.pluginmanager.register(Provider())
|
|
||||||
item.setupargs()
|
|
||||||
assert len(item.funcargs) == 1
|
|
||||||
|
|
||||||
def test_funcarg_lookup_default_gets_overriden(self, testdir):
|
|
||||||
item = testdir.getitem("def test_func(some=42, other=13): pass")
|
|
||||||
class Provider:
|
|
||||||
def pytest_funcarg__other(self, pyfuncitem):
|
|
||||||
return pyfuncitem.name
|
|
||||||
item.config.pluginmanager.register(Provider())
|
|
||||||
item.setupargs()
|
|
||||||
assert len(item.funcargs) == 1
|
|
||||||
name, value = item.funcargs.popitem()
|
|
||||||
assert name == "other"
|
|
||||||
assert value == item.name
|
|
||||||
|
|
||||||
def test_funcarg_basic(self, testdir):
|
|
||||||
item = testdir.getitem("def test_func(some, other): pass")
|
|
||||||
class Provider:
|
|
||||||
def pytest_funcarg__some(self, pyfuncitem):
|
|
||||||
return pyfuncitem.name
|
|
||||||
def pytest_funcarg__other(self, pyfuncitem):
|
|
||||||
return 42
|
|
||||||
item.config.pluginmanager.register(Provider())
|
|
||||||
item.setupargs()
|
|
||||||
assert len(item.funcargs) == 2
|
|
||||||
assert item.funcargs['some'] == "test_func"
|
|
||||||
assert item.funcargs['other'] == 42
|
|
||||||
|
|
||||||
def test_funcarg_addfinalizer(self, testdir):
|
|
||||||
item = testdir.getitem("def test_func(some): pass")
|
|
||||||
l = []
|
|
||||||
class Provider:
|
|
||||||
def pytest_funcarg__some(self, pyfuncitem):
|
|
||||||
pyfuncitem.addfinalizer(lambda: l.append(42))
|
|
||||||
return 3
|
|
||||||
item.config.pluginmanager.register(Provider())
|
|
||||||
item.setupargs()
|
|
||||||
assert len(item.funcargs) == 1
|
|
||||||
assert item.funcargs['some'] == 3
|
|
||||||
assert len(l) == 0
|
|
||||||
item.teardown()
|
|
||||||
assert len(l) == 1
|
|
||||||
assert l[0] == 42
|
|
||||||
|
|
||||||
def test_funcarg_lookup_modulelevel(self, testdir):
|
|
||||||
modcol = testdir.getmodulecol("""
|
|
||||||
def pytest_funcarg__something(pyfuncitem):
|
|
||||||
return pyfuncitem.name
|
|
||||||
|
|
||||||
class TestClass:
|
|
||||||
def test_method(self, something):
|
|
||||||
pass
|
|
||||||
def test_func(something):
|
|
||||||
pass
|
|
||||||
""")
|
|
||||||
item1, item2 = testdir.genitems([modcol])
|
|
||||||
modcol.setup()
|
|
||||||
assert modcol.config.pluginmanager.isregistered(modcol.obj)
|
|
||||||
item1.setupargs()
|
|
||||||
assert item1.funcargs['something'] == "test_method"
|
|
||||||
item2.setupargs()
|
|
||||||
assert item2.funcargs['something'] == "test_func"
|
|
||||||
modcol.teardown()
|
|
||||||
assert not modcol.config.pluginmanager.isregistered(modcol.obj)
|
|
||||||
|
|
||||||
class TestSorting:
|
class TestSorting:
|
||||||
def test_check_equality_and_cmp_basic(self, testdir):
|
def test_check_equality_and_cmp_basic(self, testdir):
|
||||||
modcol = testdir.getmodulecol("""
|
modcol = testdir.getmodulecol("""
|
||||||
|
|
|
@ -10,7 +10,7 @@ class TestTracebackCutting:
|
||||||
def test_traceback_argsetup(self, testdir):
|
def test_traceback_argsetup(self, testdir):
|
||||||
testdir.makeconftest("""
|
testdir.makeconftest("""
|
||||||
class ConftestPlugin:
|
class ConftestPlugin:
|
||||||
def pytest_funcarg__hello(self, pyfuncitem):
|
def pytest_funcarg__hello(self, request):
|
||||||
raise ValueError("xyz")
|
raise ValueError("xyz")
|
||||||
""")
|
""")
|
||||||
p = testdir.makepyfile("def test(hello): pass")
|
p = testdir.makepyfile("def test(hello): pass")
|
||||||
|
|
Loading…
Reference in New Issue