from __future__ import absolute_import, unicode_literals
import pytest
import sys
from functools import wraps
from six import iteritems as items
from . import patch
from . import mock
sentinel = object()
[docs]
class fixture_with_options(object):
"""Pytest fixture with options specified in separate decrorator.
The decorated fixture MUST take the request fixture as first argument,
but is free to use other fixtures.
Example:
@fixture_with_options()
def sftp(request,
username='test_username',
password='test_password'):
return {'username': username, 'password': password}
@sftp.options(username='foo', password='bar')
def test_foo(sftp):
assert sftp['username'] == 'foo'
assert sftp['password'] == 'bar'
"""
def __init__(self, marker_name=None):
self.marker_name = marker_name
def __call__(self, fun):
marker_name = self.marker_name or fun.__name__
@pytest.fixture()
@wraps(fun)
def _inner(request, *args, **kwargs):
marker = request.node.get_marker(marker_name)
print(request.node)
return fun(request, *args, **dict(marker.kwargs, **kwargs))
def options(*args, **kwargs):
return getattr(pytest.mark, marker_name)(*args, **kwargs)
_inner.options = options
_inner.__wrapped__ = fun
return _inner
class _patching(object):
def __init__(self, monkeypatch, request):
self.monkeypatch = monkeypatch
self.request = request
def __getattr__(self, name):
return getattr(self.monkeypatch, name)
def __call__(self, path, value=sentinel, name=None,
new=mock.MagicMock, **kwargs):
value = self._value_or_mock(value, new, name, path, **kwargs)
self.monkeypatch.setattr(path, value)
return value
def object(self, target, attribute, *args, **kwargs):
return _wrap_context(
patch.object(target, attribute, *args, **kwargs),
self.request)
def _value_or_mock(self, value, new, name, path, **kwargs):
if value is sentinel:
value = new(name=name or path.rpartition('.')[2])
for k, v in items(kwargs):
setattr(value, k, v)
return value
def setattr(self, target, name=sentinel, value=sentinel, **kwargs):
# alias to __call__ with the interface of pytest.monkeypatch.setattr
if value is sentinel:
value, name = name, None
return self(target, value, name=name)
def setitem(self, dic, name, value=sentinel, new=mock.MagicMock, **kwargs):
# same as pytest.monkeypatch.setattr but default value is MagicMock
value = self._value_or_mock(value, new, name, dic, **kwargs)
self.monkeypatch.setitem(dic, name, value)
return value
def modules(self, *mods):
modules = []
for mod in mods:
mod = mod.split('.')
modules.extend(reversed([
'.'.join(mod[:-i] if i else mod) for i in range(len(mod))
]))
modules = sorted(set(modules))
return _wrap_context(mock.module(*modules), self.request)
def _wrap_context(context, request):
ret = context.__enter__()
def fin():
context.__exit__(*sys.exc_info())
request.addfinalizer(fin)
return ret
[docs]
@pytest.fixture()
def patching(monkeypatch, request):
"""Monkeypath.setattr shortcut.
Example:
.. code-block:: python
def test_foo(patching):
# execv value here will be mock.MagicMock by default.
execv = patching('os.execv')
patching('sys.platform', 'darwin') # set concrete value
patching.setenv('DJANGO_SETTINGS_MODULE', 'x.settings')
# val will be of type mock.MagicMock by default
val = patching.setitem('path.to.dict', 'KEY')
"""
return _patching(monkeypatch, request)
class _stdouts(object):
def __init__(self, stdout, stderr):
self.stdout = stdout
self.stderr = stderr
[docs]
@pytest.fixture()
def stdouts(request):
return _stdouts(*_wrap_context(mock.stdouts(), request))