447 lines
13 KiB
Python
447 lines
13 KiB
Python
# mypy: allow-untyped-defs
|
|
from _pytest._code import getfslineno
|
|
from _pytest.fixtures import getfixturemarker
|
|
from _pytest.pytester import Pytester
|
|
from _pytest.python import Function
|
|
import pytest
|
|
|
|
|
|
def test_wrapped_getfslineno() -> None:
|
|
def func():
|
|
pass
|
|
|
|
def wrap(f):
|
|
func.__wrapped__ = f # type: ignore
|
|
func.patchings = ["qwe"] # type: ignore
|
|
return func
|
|
|
|
@wrap
|
|
def wrapped_func(x, y, z):
|
|
pass
|
|
|
|
fs, lineno = getfslineno(wrapped_func)
|
|
fs2, lineno2 = getfslineno(wrap)
|
|
assert lineno > lineno2, "getfslineno does not unwrap correctly"
|
|
|
|
|
|
class TestMockDecoration:
|
|
def test_wrapped_getfuncargnames(self) -> None:
|
|
from _pytest.compat import getfuncargnames
|
|
|
|
def wrap(f):
|
|
def func():
|
|
pass
|
|
|
|
func.__wrapped__ = f # type: ignore
|
|
return func
|
|
|
|
@wrap
|
|
def f(x):
|
|
pass
|
|
|
|
values = getfuncargnames(f)
|
|
assert values == ("x",)
|
|
|
|
def test_getfuncargnames_patching(self):
|
|
from unittest.mock import patch
|
|
|
|
from _pytest.compat import getfuncargnames
|
|
|
|
class T:
|
|
def original(self, x, y, z):
|
|
pass
|
|
|
|
@patch.object(T, "original")
|
|
def f(x, y, z):
|
|
pass
|
|
|
|
values = getfuncargnames(f)
|
|
assert values == ("y", "z")
|
|
|
|
def test_unittest_mock(self, pytester: Pytester) -> None:
|
|
pytester.makepyfile(
|
|
"""
|
|
import unittest.mock
|
|
class T(unittest.TestCase):
|
|
@unittest.mock.patch("os.path.abspath")
|
|
def test_hello(self, abspath):
|
|
import os
|
|
os.path.abspath("hello")
|
|
abspath.assert_any_call("hello")
|
|
"""
|
|
)
|
|
reprec = pytester.inline_run()
|
|
reprec.assertoutcome(passed=1)
|
|
|
|
def test_unittest_mock_and_fixture(self, pytester: Pytester) -> None:
|
|
pytester.makepyfile(
|
|
"""
|
|
import os.path
|
|
import unittest.mock
|
|
import pytest
|
|
|
|
@pytest.fixture
|
|
def inject_me():
|
|
pass
|
|
|
|
@unittest.mock.patch.object(os.path, "abspath",
|
|
new=unittest.mock.MagicMock)
|
|
def test_hello(inject_me):
|
|
import os
|
|
os.path.abspath("hello")
|
|
"""
|
|
)
|
|
reprec = pytester.inline_run()
|
|
reprec.assertoutcome(passed=1)
|
|
|
|
def test_unittest_mock_and_pypi_mock(self, pytester: Pytester) -> None:
|
|
pytest.importorskip("mock", "1.0.1")
|
|
pytester.makepyfile(
|
|
"""
|
|
import mock
|
|
import unittest.mock
|
|
class TestBoth(object):
|
|
@unittest.mock.patch("os.path.abspath")
|
|
def test_hello(self, abspath):
|
|
import os
|
|
os.path.abspath("hello")
|
|
abspath.assert_any_call("hello")
|
|
|
|
@mock.patch("os.path.abspath")
|
|
def test_hello_mock(self, abspath):
|
|
import os
|
|
os.path.abspath("hello")
|
|
abspath.assert_any_call("hello")
|
|
"""
|
|
)
|
|
reprec = pytester.inline_run()
|
|
reprec.assertoutcome(passed=2)
|
|
|
|
def test_mock_sentinel_check_against_numpy_like(self, pytester: Pytester) -> None:
|
|
"""Ensure our function that detects mock arguments compares against sentinels using
|
|
identity to circumvent objects which can't be compared with equality against others
|
|
in a truth context, like with numpy arrays (#5606).
|
|
"""
|
|
pytester.makepyfile(
|
|
dummy="""
|
|
class NumpyLike:
|
|
def __init__(self, value):
|
|
self.value = value
|
|
def __eq__(self, other):
|
|
raise ValueError("like numpy, cannot compare against others for truth")
|
|
FOO = NumpyLike(10)
|
|
"""
|
|
)
|
|
pytester.makepyfile(
|
|
"""
|
|
from unittest.mock import patch
|
|
import dummy
|
|
class Test(object):
|
|
@patch("dummy.FOO", new=dummy.NumpyLike(50))
|
|
def test_hello(self):
|
|
assert dummy.FOO.value == 50
|
|
"""
|
|
)
|
|
reprec = pytester.inline_run()
|
|
reprec.assertoutcome(passed=1)
|
|
|
|
def test_mock(self, pytester: Pytester) -> None:
|
|
pytest.importorskip("mock", "1.0.1")
|
|
pytester.makepyfile(
|
|
"""
|
|
import os
|
|
import unittest
|
|
import mock
|
|
|
|
class T(unittest.TestCase):
|
|
@mock.patch("os.path.abspath")
|
|
def test_hello(self, abspath):
|
|
os.path.abspath("hello")
|
|
abspath.assert_any_call("hello")
|
|
def mock_basename(path):
|
|
return "mock_basename"
|
|
@mock.patch("os.path.abspath")
|
|
@mock.patch("os.path.normpath")
|
|
@mock.patch("os.path.basename", new=mock_basename)
|
|
def test_someting(normpath, abspath, tmp_path):
|
|
abspath.return_value = "this"
|
|
os.path.normpath(os.path.abspath("hello"))
|
|
normpath.assert_any_call("this")
|
|
assert os.path.basename("123") == "mock_basename"
|
|
"""
|
|
)
|
|
reprec = pytester.inline_run()
|
|
reprec.assertoutcome(passed=2)
|
|
calls = reprec.getcalls("pytest_runtest_logreport")
|
|
funcnames = [
|
|
call.report.location[2] for call in calls if call.report.when == "call"
|
|
]
|
|
assert funcnames == ["T.test_hello", "test_someting"]
|
|
|
|
def test_mock_sorting(self, pytester: Pytester) -> None:
|
|
pytest.importorskip("mock", "1.0.1")
|
|
pytester.makepyfile(
|
|
"""
|
|
import os
|
|
import mock
|
|
|
|
@mock.patch("os.path.abspath")
|
|
def test_one(abspath):
|
|
pass
|
|
@mock.patch("os.path.abspath")
|
|
def test_two(abspath):
|
|
pass
|
|
@mock.patch("os.path.abspath")
|
|
def test_three(abspath):
|
|
pass
|
|
"""
|
|
)
|
|
reprec = pytester.inline_run()
|
|
calls = reprec.getreports("pytest_runtest_logreport")
|
|
calls = [x for x in calls if x.when == "call"]
|
|
names = [x.nodeid.split("::")[-1] for x in calls]
|
|
assert names == ["test_one", "test_two", "test_three"]
|
|
|
|
def test_mock_double_patch_issue473(self, pytester: Pytester) -> None:
|
|
pytest.importorskip("mock", "1.0.1")
|
|
pytester.makepyfile(
|
|
"""
|
|
from mock import patch
|
|
from pytest import mark
|
|
|
|
@patch('os.getcwd')
|
|
@patch('os.path')
|
|
@mark.slow
|
|
class TestSimple(object):
|
|
def test_simple_thing(self, mock_path, mock_getcwd):
|
|
pass
|
|
"""
|
|
)
|
|
reprec = pytester.inline_run()
|
|
reprec.assertoutcome(passed=1)
|
|
|
|
|
|
class TestReRunTests:
|
|
def test_rerun(self, pytester: Pytester) -> None:
|
|
pytester.makeconftest(
|
|
"""
|
|
from _pytest.runner import runtestprotocol
|
|
def pytest_runtest_protocol(item, nextitem):
|
|
runtestprotocol(item, log=False, nextitem=nextitem)
|
|
runtestprotocol(item, log=True, nextitem=nextitem)
|
|
"""
|
|
)
|
|
pytester.makepyfile(
|
|
"""
|
|
import pytest
|
|
count = 0
|
|
req = None
|
|
@pytest.fixture
|
|
def fix(request):
|
|
global count, req
|
|
assert request != req
|
|
req = request
|
|
print("fix count %s" % count)
|
|
count += 1
|
|
def test_fix(fix):
|
|
pass
|
|
"""
|
|
)
|
|
result = pytester.runpytest("-s")
|
|
result.stdout.fnmatch_lines(
|
|
"""
|
|
*fix count 0*
|
|
*fix count 1*
|
|
"""
|
|
)
|
|
result.stdout.fnmatch_lines(
|
|
"""
|
|
*2 passed*
|
|
"""
|
|
)
|
|
|
|
|
|
def test_pytestconfig_is_session_scoped() -> None:
|
|
from _pytest.fixtures import pytestconfig
|
|
|
|
marker = getfixturemarker(pytestconfig)
|
|
assert marker is not None
|
|
assert marker.scope == "session"
|
|
|
|
|
|
class TestNoselikeTestAttribute:
|
|
def test_module_with_global_test(self, pytester: Pytester) -> None:
|
|
pytester.makepyfile(
|
|
"""
|
|
__test__ = False
|
|
def test_hello():
|
|
pass
|
|
"""
|
|
)
|
|
reprec = pytester.inline_run()
|
|
assert not reprec.getfailedcollections()
|
|
calls = reprec.getreports("pytest_runtest_logreport")
|
|
assert not calls
|
|
|
|
def test_class_and_method(self, pytester: Pytester) -> None:
|
|
pytester.makepyfile(
|
|
"""
|
|
__test__ = True
|
|
def test_func():
|
|
pass
|
|
test_func.__test__ = False
|
|
|
|
class TestSome(object):
|
|
__test__ = False
|
|
def test_method(self):
|
|
pass
|
|
"""
|
|
)
|
|
reprec = pytester.inline_run()
|
|
assert not reprec.getfailedcollections()
|
|
calls = reprec.getreports("pytest_runtest_logreport")
|
|
assert not calls
|
|
|
|
def test_unittest_class(self, pytester: Pytester) -> None:
|
|
pytester.makepyfile(
|
|
"""
|
|
import unittest
|
|
class TC(unittest.TestCase):
|
|
def test_1(self):
|
|
pass
|
|
class TC2(unittest.TestCase):
|
|
__test__ = False
|
|
def test_2(self):
|
|
pass
|
|
"""
|
|
)
|
|
reprec = pytester.inline_run()
|
|
assert not reprec.getfailedcollections()
|
|
call = reprec.getcalls("pytest_collection_modifyitems")[0]
|
|
assert len(call.items) == 1
|
|
assert call.items[0].cls.__name__ == "TC"
|
|
|
|
def test_class_with_nasty_getattr(self, pytester: Pytester) -> None:
|
|
"""Make sure we handle classes with a custom nasty __getattr__ right.
|
|
|
|
With a custom __getattr__ which e.g. returns a function (like with a
|
|
RPC wrapper), we shouldn't assume this meant "__test__ = True".
|
|
"""
|
|
# https://github.com/pytest-dev/pytest/issues/1204
|
|
pytester.makepyfile(
|
|
"""
|
|
class MetaModel(type):
|
|
|
|
def __getattr__(cls, key):
|
|
return lambda: None
|
|
|
|
|
|
BaseModel = MetaModel('Model', (), {})
|
|
|
|
|
|
class Model(BaseModel):
|
|
|
|
__metaclass__ = MetaModel
|
|
|
|
def test_blah(self):
|
|
pass
|
|
"""
|
|
)
|
|
reprec = pytester.inline_run()
|
|
assert not reprec.getfailedcollections()
|
|
call = reprec.getcalls("pytest_collection_modifyitems")[0]
|
|
assert not call.items
|
|
|
|
|
|
class TestParameterize:
|
|
"""#351"""
|
|
|
|
def test_idfn_marker(self, pytester: Pytester) -> None:
|
|
pytester.makepyfile(
|
|
"""
|
|
import pytest
|
|
|
|
def idfn(param):
|
|
if param == 0:
|
|
return 'spam'
|
|
elif param == 1:
|
|
return 'ham'
|
|
else:
|
|
return None
|
|
|
|
@pytest.mark.parametrize('a,b', [(0, 2), (1, 2)], ids=idfn)
|
|
def test_params(a, b):
|
|
pass
|
|
"""
|
|
)
|
|
res = pytester.runpytest("--collect-only")
|
|
res.stdout.fnmatch_lines(["*spam-2*", "*ham-2*"])
|
|
|
|
def test_idfn_fixture(self, pytester: Pytester) -> None:
|
|
pytester.makepyfile(
|
|
"""
|
|
import pytest
|
|
|
|
def idfn(param):
|
|
if param == 0:
|
|
return 'spam'
|
|
elif param == 1:
|
|
return 'ham'
|
|
else:
|
|
return None
|
|
|
|
@pytest.fixture(params=[0, 1], ids=idfn)
|
|
def a(request):
|
|
return request.param
|
|
|
|
@pytest.fixture(params=[1, 2], ids=idfn)
|
|
def b(request):
|
|
return request.param
|
|
|
|
def test_params(a, b):
|
|
pass
|
|
"""
|
|
)
|
|
res = pytester.runpytest("--collect-only")
|
|
res.stdout.fnmatch_lines(["*spam-2*", "*ham-2*"])
|
|
|
|
|
|
def test_function_instance(pytester: Pytester) -> None:
|
|
items = pytester.getitems(
|
|
"""
|
|
def test_func(): pass
|
|
|
|
class TestIt:
|
|
def test_method(self): pass
|
|
|
|
@classmethod
|
|
def test_class(cls): pass
|
|
|
|
@staticmethod
|
|
def test_static(): pass
|
|
"""
|
|
)
|
|
assert len(items) == 4
|
|
|
|
assert isinstance(items[0], Function)
|
|
assert items[0].name == "test_func"
|
|
assert items[0].instance is None
|
|
|
|
assert isinstance(items[1], Function)
|
|
assert items[1].name == "test_method"
|
|
assert items[1].instance is not None
|
|
assert items[1].instance.__class__.__name__ == "TestIt"
|
|
|
|
# Even class and static methods get an instance!
|
|
# This is the instance used for bound fixture methods, which
|
|
# class/staticmethod tests are perfectly able to request.
|
|
assert isinstance(items[2], Function)
|
|
assert items[2].name == "test_class"
|
|
assert items[2].instance is not None
|
|
|
|
assert isinstance(items[3], Function)
|
|
assert items[3].name == "test_static"
|
|
assert items[3].instance is not None
|
|
|
|
assert items[1].instance is not items[2].instance is not items[3].instance
|