478 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			478 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Python
		
	
	
	
| from typing import Any
 | |
| 
 | |
| import pytest
 | |
| from _pytest import runner
 | |
| from _pytest._code import getfslineno
 | |
| from _pytest.fixtures import getfixturemarker
 | |
| from _pytest.pytester import Pytester
 | |
| 
 | |
| 
 | |
| class TestOEJSKITSpecials:
 | |
|     def test_funcarg_non_pycollectobj(
 | |
|         self, pytester: Pytester, recwarn
 | |
|     ) -> None:  # rough jstests usage
 | |
|         pytester.makeconftest(
 | |
|             """
 | |
|             import pytest
 | |
|             def pytest_pycollect_makeitem(collector, name, obj):
 | |
|                 if name == "MyClass":
 | |
|                     return MyCollector.from_parent(collector, name=name)
 | |
|             class MyCollector(pytest.Collector):
 | |
|                 def reportinfo(self):
 | |
|                     return self.fspath, 3, "xyz"
 | |
|         """
 | |
|         )
 | |
|         modcol = pytester.getmodulecol(
 | |
|             """
 | |
|             import pytest
 | |
|             @pytest.fixture
 | |
|             def arg1(request):
 | |
|                 return 42
 | |
|             class MyClass(object):
 | |
|                 pass
 | |
|         """
 | |
|         )
 | |
|         # this hook finds funcarg factories
 | |
|         rep = runner.collect_one_node(collector=modcol)
 | |
|         # TODO: Don't treat as Any.
 | |
|         clscol: Any = rep.result[0]
 | |
|         clscol.obj = lambda arg1: None
 | |
|         clscol.funcargs = {}
 | |
|         pytest._fillfuncargs(clscol)
 | |
|         assert clscol.funcargs["arg1"] == 42
 | |
| 
 | |
|     def test_autouse_fixture(
 | |
|         self, pytester: Pytester, recwarn
 | |
|     ) -> None:  # rough jstests usage
 | |
|         pytester.makeconftest(
 | |
|             """
 | |
|             import pytest
 | |
|             def pytest_pycollect_makeitem(collector, name, obj):
 | |
|                 if name == "MyClass":
 | |
|                     return MyCollector.from_parent(collector, name=name)
 | |
|             class MyCollector(pytest.Collector):
 | |
|                 def reportinfo(self):
 | |
|                     return self.fspath, 3, "xyz"
 | |
|         """
 | |
|         )
 | |
|         modcol = pytester.getmodulecol(
 | |
|             """
 | |
|             import pytest
 | |
|             @pytest.fixture(autouse=True)
 | |
|             def hello():
 | |
|                 pass
 | |
|             @pytest.fixture
 | |
|             def arg1(request):
 | |
|                 return 42
 | |
|             class MyClass(object):
 | |
|                 pass
 | |
|         """
 | |
|         )
 | |
|         # this hook finds funcarg factories
 | |
|         rep = runner.collect_one_node(modcol)
 | |
|         # TODO: Don't treat as Any.
 | |
|         clscol: Any = rep.result[0]
 | |
|         clscol.obj = lambda: None
 | |
|         clscol.funcargs = {}
 | |
|         pytest._fillfuncargs(clscol)
 | |
|         assert not clscol.funcargs
 | |
| 
 | |
| 
 | |
| 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 _pytest.compat import getfuncargnames
 | |
|         from unittest.mock import patch
 | |
| 
 | |
|         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*"])
 |