test_pytest_mock.py 36 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307
  1. import os
  2. import platform
  3. import re
  4. import sys
  5. import warnings
  6. from contextlib import contextmanager
  7. from typing import Any
  8. from typing import Callable
  9. from typing import Generator
  10. from typing import Tuple
  11. from typing import Type
  12. from unittest.mock import AsyncMock
  13. from unittest.mock import MagicMock
  14. import pytest
  15. from pytest_mock import MockerFixture
  16. from pytest_mock import PytestMockWarning
  17. pytest_plugins = "pytester"
  18. # could not make some of the tests work on PyPy, patches are welcome!
  19. skip_pypy = pytest.mark.skipif(
  20. platform.python_implementation() == "PyPy", reason="could not make it work on pypy"
  21. )
  22. # Python 3.11.7 changed the output formatting, https://github.com/python/cpython/issues/111019
  23. NEWEST_FORMATTING = sys.version_info >= (3, 11, 7)
  24. @pytest.fixture
  25. def needs_assert_rewrite(pytestconfig):
  26. """
  27. Fixture which skips requesting test if assertion rewrite is disabled (#102)
  28. Making this a fixture to avoid accessing pytest's config in the global context.
  29. """
  30. option = pytestconfig.getoption("assertmode")
  31. if option != "rewrite":
  32. pytest.skip(
  33. "this test needs assertion rewrite to work but current option "
  34. 'is "{}"'.format(option)
  35. )
  36. class UnixFS:
  37. """
  38. Wrapper to os functions to simulate a Unix file system, used for testing
  39. the mock fixture.
  40. """
  41. @classmethod
  42. def rm(cls, filename):
  43. os.remove(filename)
  44. @classmethod
  45. def ls(cls, path):
  46. return os.listdir(path)
  47. class TestObject:
  48. """
  49. Class that is used for testing create_autospec with child mocks
  50. """
  51. def run(self) -> str:
  52. return "not mocked"
  53. @pytest.fixture
  54. def check_unix_fs_mocked(
  55. tmpdir: Any, mocker: MockerFixture
  56. ) -> Callable[[Any, Any], None]:
  57. """
  58. performs a standard test in a UnixFS, assuming that both `os.remove` and
  59. `os.listdir` have been mocked previously.
  60. """
  61. def check(mocked_rm, mocked_ls):
  62. assert mocked_rm is os.remove
  63. assert mocked_ls is os.listdir
  64. file_name = tmpdir / "foo.txt"
  65. file_name.ensure()
  66. UnixFS.rm(str(file_name))
  67. mocked_rm.assert_called_once_with(str(file_name))
  68. assert os.path.isfile(str(file_name))
  69. mocked_ls.return_value = ["bar.txt"]
  70. assert UnixFS.ls(str(tmpdir)) == ["bar.txt"]
  71. mocked_ls.assert_called_once_with(str(tmpdir))
  72. mocker.stopall()
  73. assert UnixFS.ls(str(tmpdir)) == ["foo.txt"]
  74. UnixFS.rm(str(file_name))
  75. assert not os.path.isfile(str(file_name))
  76. return check
  77. def mock_using_patch_object(mocker: MockerFixture) -> Tuple[MagicMock, MagicMock]:
  78. return mocker.patch.object(os, "remove"), mocker.patch.object(os, "listdir")
  79. def mock_using_patch(mocker: MockerFixture) -> Tuple[MagicMock, MagicMock]:
  80. return mocker.patch("os.remove"), mocker.patch("os.listdir")
  81. def mock_using_patch_multiple(mocker: MockerFixture) -> Tuple[MagicMock, MagicMock]:
  82. r = mocker.patch.multiple("os", remove=mocker.DEFAULT, listdir=mocker.DEFAULT)
  83. return r["remove"], r["listdir"]
  84. @pytest.mark.parametrize(
  85. "mock_fs", [mock_using_patch_object, mock_using_patch, mock_using_patch_multiple]
  86. )
  87. def test_mock_patches(
  88. mock_fs: Any,
  89. mocker: MockerFixture,
  90. check_unix_fs_mocked: Callable[[Any, Any], None],
  91. ) -> None:
  92. """
  93. Installs mocks into `os` functions and performs a standard testing of
  94. mock functionality. We parametrize different mock methods to ensure
  95. all (intended, at least) mock API is covered.
  96. """
  97. # mock it twice on purpose to ensure we unmock it correctly later
  98. mock_fs(mocker)
  99. mocked_rm, mocked_ls = mock_fs(mocker)
  100. check_unix_fs_mocked(mocked_rm, mocked_ls)
  101. mocker.resetall()
  102. mocker.stopall()
  103. def test_mock_patch_dict(mocker: MockerFixture) -> None:
  104. """
  105. Testing
  106. :param mock:
  107. """
  108. x = {"original": 1}
  109. mocker.patch.dict(x, values=[("new", 10)], clear=True)
  110. assert x == {"new": 10}
  111. mocker.stopall()
  112. assert x == {"original": 1}
  113. def test_mock_patch_dict_resetall(mocker: MockerFixture) -> None:
  114. """
  115. We can call resetall after patching a dict.
  116. :param mock:
  117. """
  118. x = {"original": 1}
  119. mocker.patch.dict(x, values=[("new", 10)], clear=True)
  120. assert x == {"new": 10}
  121. mocker.resetall()
  122. assert x == {"new": 10}
  123. @pytest.mark.parametrize(
  124. "name",
  125. [
  126. "ANY",
  127. "call",
  128. "MagicMock",
  129. "Mock",
  130. "mock_open",
  131. "NonCallableMagicMock",
  132. "NonCallableMock",
  133. "PropertyMock",
  134. "sentinel",
  135. "seal",
  136. ],
  137. )
  138. def test_mocker_aliases(name: str, pytestconfig: Any) -> None:
  139. from pytest_mock._util import get_mock_module
  140. mock_module = get_mock_module(pytestconfig)
  141. mocker = MockerFixture(pytestconfig)
  142. assert getattr(mocker, name) is getattr(mock_module, name)
  143. def test_mocker_resetall(mocker: MockerFixture) -> None:
  144. listdir = mocker.patch("os.listdir", return_value="foo")
  145. open = mocker.patch("os.open", side_effect=["bar", "baz"])
  146. mocked_object = mocker.create_autospec(TestObject)
  147. mocked_object.run.return_value = "mocked"
  148. assert listdir("/tmp") == "foo"
  149. assert open("/tmp/foo.txt") == "bar"
  150. assert mocked_object.run() == "mocked"
  151. listdir.assert_called_once_with("/tmp")
  152. open.assert_called_once_with("/tmp/foo.txt")
  153. mocked_object.run.assert_called_once()
  154. mocker.resetall()
  155. assert not listdir.called
  156. assert not open.called
  157. assert not mocked_object.called
  158. assert listdir.return_value == "foo"
  159. assert list(open.side_effect) == ["baz"]
  160. assert mocked_object.run.return_value == "mocked"
  161. mocker.resetall(return_value=True, side_effect=True)
  162. assert isinstance(listdir.return_value, mocker.Mock)
  163. assert open.side_effect is None
  164. if sys.version_info >= (3, 9):
  165. # The reset on child mocks have been implemented in 3.9
  166. # https://bugs.python.org/issue38932
  167. assert mocked_object.run.return_value != "mocked"
  168. class TestMockerStub:
  169. def test_call(self, mocker: MockerFixture) -> None:
  170. stub = mocker.stub()
  171. stub("foo", "bar")
  172. stub.assert_called_once_with("foo", "bar")
  173. def test_repr_with_no_name(self, mocker: MockerFixture) -> None:
  174. stub = mocker.stub()
  175. assert "name" not in repr(stub)
  176. def test_repr_with_name(self, mocker: MockerFixture) -> None:
  177. test_name = "funny walk"
  178. stub = mocker.stub(name=test_name)
  179. assert f"name={test_name!r}" in repr(stub)
  180. def __test_failure_message(self, mocker: MockerFixture, **kwargs: Any) -> None:
  181. expected_name = kwargs.get("name") or "mock"
  182. if NEWEST_FORMATTING:
  183. msg = "expected call not found.\nExpected: {0}()\n Actual: not called."
  184. else:
  185. msg = "expected call not found.\nExpected: {0}()\nActual: not called."
  186. expected_message = msg.format(expected_name)
  187. stub = mocker.stub(**kwargs)
  188. with pytest.raises(AssertionError, match=re.escape(expected_message)):
  189. stub.assert_called_with()
  190. def test_failure_message_with_no_name(self, mocker: MagicMock) -> None:
  191. self.__test_failure_message(mocker)
  192. @pytest.mark.parametrize("name", (None, "", "f", "The Castle of aaarrrrggh"))
  193. def test_failure_message_with_name(self, mocker: MagicMock, name: str) -> None:
  194. self.__test_failure_message(mocker, name=name)
  195. def test_async_stub_type(self, mocker: MockerFixture) -> None:
  196. assert isinstance(mocker.async_stub(), AsyncMock)
  197. def test_instance_method_spy(mocker: MockerFixture) -> None:
  198. class Foo:
  199. def bar(self, arg):
  200. return arg * 2
  201. foo = Foo()
  202. other = Foo()
  203. spy = mocker.spy(foo, "bar")
  204. assert foo.bar(arg=10) == 20
  205. assert other.bar(arg=10) == 20
  206. foo.bar.assert_called_once_with(arg=10) # type:ignore[attr-defined]
  207. assert foo.bar.spy_return == 20 # type:ignore[attr-defined]
  208. assert foo.bar.spy_return_list == [20] # type:ignore[attr-defined]
  209. spy.assert_called_once_with(arg=10)
  210. assert spy.spy_return == 20
  211. assert foo.bar(arg=11) == 22
  212. assert foo.bar(arg=12) == 24
  213. assert spy.spy_return == 24
  214. assert spy.spy_return_list == [20, 22, 24]
  215. # Ref: https://docs.python.org/3/library/exceptions.html#exception-hierarchy
  216. @pytest.mark.parametrize(
  217. "exc_cls",
  218. (
  219. BaseException,
  220. Exception,
  221. GeneratorExit, # BaseException
  222. KeyboardInterrupt, # BaseException
  223. RuntimeError, # regular Exception
  224. SystemExit, # BaseException
  225. ),
  226. )
  227. def test_instance_method_spy_exception(
  228. exc_cls: Type[BaseException],
  229. mocker: MockerFixture,
  230. ) -> None:
  231. class Foo:
  232. def bar(self, arg):
  233. raise exc_cls(f"Error with {arg}")
  234. foo = Foo()
  235. spy = mocker.spy(foo, "bar")
  236. expected_calls = []
  237. for i, v in enumerate([10, 20]):
  238. with pytest.raises(exc_cls, match=f"Error with {v}"):
  239. foo.bar(arg=v)
  240. expected_calls.append(mocker.call(arg=v))
  241. assert foo.bar.call_args_list == expected_calls # type:ignore[attr-defined]
  242. assert str(spy.spy_exception) == f"Error with {v}"
  243. def test_instance_class_static_method_spy_autospec_true(mocker: MockerFixture) -> None:
  244. class Foo:
  245. def bar(self, arg):
  246. return arg * 2
  247. @classmethod
  248. def baz(cls, arg):
  249. return arg * 2
  250. @staticmethod
  251. def qux(arg):
  252. return arg * 2
  253. foo = Foo()
  254. instance_method_spy = mocker.spy(foo, "bar")
  255. with pytest.raises(
  256. AttributeError, match="'function' object has no attribute 'fake_assert_method'"
  257. ):
  258. instance_method_spy.fake_assert_method(arg=5)
  259. class_method_spy = mocker.spy(Foo, "baz")
  260. with pytest.raises(
  261. AttributeError, match="Mock object has no attribute 'fake_assert_method'"
  262. ):
  263. class_method_spy.fake_assert_method(arg=5)
  264. static_method_spy = mocker.spy(Foo, "qux")
  265. with pytest.raises(
  266. AttributeError, match="Mock object has no attribute 'fake_assert_method'"
  267. ):
  268. static_method_spy.fake_assert_method(arg=5)
  269. def test_spy_reset(mocker: MockerFixture) -> None:
  270. class Foo:
  271. def bar(self, x):
  272. if x == 0:
  273. raise ValueError("invalid x")
  274. return x * 3
  275. spy = mocker.spy(Foo, "bar")
  276. assert spy.spy_return is None
  277. assert spy.spy_return_list == []
  278. assert spy.spy_exception is None
  279. Foo().bar(10)
  280. assert spy.spy_return == 30
  281. assert spy.spy_return_list == [30]
  282. assert spy.spy_exception is None
  283. # Testing spy can still be reset (#237).
  284. mocker.resetall()
  285. with pytest.raises(ValueError):
  286. Foo().bar(0)
  287. assert spy.spy_return is None
  288. assert spy.spy_return_list == []
  289. assert str(spy.spy_exception) == "invalid x"
  290. Foo().bar(15)
  291. assert spy.spy_return == 45
  292. assert spy.spy_return_list == [45]
  293. assert spy.spy_exception is None
  294. @skip_pypy
  295. def test_instance_method_by_class_spy(mocker: MockerFixture) -> None:
  296. class Foo:
  297. def bar(self, arg):
  298. return arg * 2
  299. spy = mocker.spy(Foo, "bar")
  300. foo = Foo()
  301. other = Foo()
  302. assert foo.bar(arg=10) == 20
  303. assert other.bar(arg=10) == 20
  304. calls = [mocker.call(foo, arg=10), mocker.call(other, arg=10)]
  305. assert spy.call_args_list == calls
  306. @skip_pypy
  307. def test_instance_method_by_subclass_spy(mocker: MockerFixture) -> None:
  308. class Base:
  309. def bar(self, arg):
  310. return arg * 2
  311. class Foo(Base):
  312. pass
  313. spy = mocker.spy(Foo, "bar")
  314. foo = Foo()
  315. other = Foo()
  316. assert foo.bar(arg=10) == 20
  317. assert other.bar(arg=10) == 20
  318. calls = [mocker.call(foo, arg=10), mocker.call(other, arg=10)]
  319. assert spy.call_args_list == calls
  320. assert spy.spy_return == 20
  321. assert spy.spy_return_list == [20, 20]
  322. @skip_pypy
  323. def test_class_method_spy(mocker: MockerFixture) -> None:
  324. class Foo:
  325. @classmethod
  326. def bar(cls, arg):
  327. return arg * 2
  328. spy = mocker.spy(Foo, "bar")
  329. assert Foo.bar(arg=10) == 20
  330. Foo.bar.assert_called_once_with(arg=10) # type:ignore[attr-defined]
  331. assert Foo.bar.spy_return == 20 # type:ignore[attr-defined]
  332. assert Foo.bar.spy_return_list == [20] # type:ignore[attr-defined]
  333. spy.assert_called_once_with(arg=10)
  334. assert spy.spy_return == 20
  335. assert spy.spy_return_list == [20]
  336. @skip_pypy
  337. def test_class_method_subclass_spy(mocker: MockerFixture) -> None:
  338. class Base:
  339. @classmethod
  340. def bar(self, arg):
  341. return arg * 2
  342. class Foo(Base):
  343. pass
  344. spy = mocker.spy(Foo, "bar")
  345. assert Foo.bar(arg=10) == 20
  346. Foo.bar.assert_called_once_with(arg=10) # type:ignore[attr-defined]
  347. assert Foo.bar.spy_return == 20 # type:ignore[attr-defined]
  348. assert Foo.bar.spy_return_list == [20] # type:ignore[attr-defined]
  349. spy.assert_called_once_with(arg=10)
  350. assert spy.spy_return == 20
  351. assert spy.spy_return_list == [20]
  352. @skip_pypy
  353. def test_class_method_with_metaclass_spy(mocker: MockerFixture) -> None:
  354. class MetaFoo(type):
  355. pass
  356. class Foo:
  357. __metaclass__ = MetaFoo
  358. @classmethod
  359. def bar(cls, arg):
  360. return arg * 2
  361. spy = mocker.spy(Foo, "bar")
  362. assert Foo.bar(arg=10) == 20
  363. Foo.bar.assert_called_once_with(arg=10) # type:ignore[attr-defined]
  364. assert Foo.bar.spy_return == 20 # type:ignore[attr-defined]
  365. assert Foo.bar.spy_return_list == [20] # type:ignore[attr-defined]
  366. spy.assert_called_once_with(arg=10)
  367. assert spy.spy_return == 20
  368. assert spy.spy_return_list == [20]
  369. @skip_pypy
  370. def test_static_method_spy(mocker: MockerFixture) -> None:
  371. class Foo:
  372. @staticmethod
  373. def bar(arg):
  374. return arg * 2
  375. spy = mocker.spy(Foo, "bar")
  376. assert Foo.bar(arg=10) == 20
  377. Foo.bar.assert_called_once_with(arg=10) # type:ignore[attr-defined]
  378. assert Foo.bar.spy_return == 20 # type:ignore[attr-defined]
  379. assert Foo.bar.spy_return_list == [20] # type:ignore[attr-defined]
  380. spy.assert_called_once_with(arg=10)
  381. assert spy.spy_return == 20
  382. assert spy.spy_return_list == [20]
  383. @skip_pypy
  384. def test_static_method_subclass_spy(mocker: MockerFixture) -> None:
  385. class Base:
  386. @staticmethod
  387. def bar(arg):
  388. return arg * 2
  389. class Foo(Base):
  390. pass
  391. spy = mocker.spy(Foo, "bar")
  392. assert Foo.bar(arg=10) == 20
  393. Foo.bar.assert_called_once_with(arg=10) # type:ignore[attr-defined]
  394. assert Foo.bar.spy_return == 20 # type:ignore[attr-defined]
  395. assert Foo.bar.spy_return_list == [20] # type:ignore[attr-defined]
  396. spy.assert_called_once_with(arg=10)
  397. assert spy.spy_return == 20
  398. assert spy.spy_return_list == [20]
  399. @pytest.mark.skip("Skip testdir")
  400. def test_callable_like_spy(testdir: Any, mocker: MockerFixture) -> None:
  401. testdir.makepyfile(
  402. uut="""
  403. class CallLike(object):
  404. def __call__(self, x):
  405. return x * 2
  406. call_like = CallLike()
  407. """
  408. )
  409. testdir.syspathinsert()
  410. uut = __import__("uut")
  411. spy = mocker.spy(uut, "call_like")
  412. uut.call_like(10)
  413. spy.assert_called_once_with(10)
  414. assert spy.spy_return == 20
  415. assert spy.spy_return_list == [20]
  416. async def test_instance_async_method_spy(mocker: MockerFixture) -> None:
  417. class Foo:
  418. async def bar(self, arg):
  419. return arg * 2
  420. foo = Foo()
  421. spy = mocker.spy(foo, "bar")
  422. result = await foo.bar(10)
  423. spy.assert_called_once_with(10)
  424. assert result == 20
  425. @contextmanager
  426. def assert_traceback() -> Generator[None, None, None]:
  427. """
  428. Assert that this file is at the top of the filtered traceback
  429. """
  430. try:
  431. yield
  432. except AssertionError as e:
  433. assert e.__traceback__.tb_frame.f_code.co_filename == __file__ # type:ignore
  434. else:
  435. raise AssertionError("DID NOT RAISE")
  436. @contextmanager
  437. def assert_argument_introspection(left: Any, right: Any) -> Generator[None, None, None]:
  438. """
  439. Assert detailed argument introspection is used
  440. """
  441. try:
  442. yield
  443. except AssertionError as e:
  444. # this may be a bit too assuming, but seems nicer then hard-coding
  445. import _pytest.assertion.util as util
  446. # NOTE: we assert with either verbose or not, depending on how our own
  447. # test was run by examining sys.argv
  448. verbose = any(a.startswith("-v") for a in sys.argv)
  449. if int(pytest.__version__.split(".")[0]) < 8:
  450. expected = "\n ".join(util._compare_eq_iterable(left, right, verbose)) # type:ignore[arg-type]
  451. else:
  452. expected = "\n ".join(
  453. util._compare_eq_iterable(left, right, lambda t, *_: t, verbose)
  454. )
  455. assert expected in str(e)
  456. else:
  457. raise AssertionError("DID NOT RAISE")
  458. def test_assert_not_called_wrapper(mocker: MockerFixture) -> None:
  459. stub = mocker.stub()
  460. stub.assert_not_called()
  461. stub()
  462. with assert_traceback():
  463. stub.assert_not_called()
  464. def test_assert_called_with_wrapper(mocker: MockerFixture) -> None:
  465. stub = mocker.stub()
  466. stub("foo")
  467. stub.assert_called_with("foo")
  468. with assert_traceback():
  469. stub.assert_called_with("bar")
  470. def test_assert_called_once_with_wrapper(mocker: MockerFixture) -> None:
  471. stub = mocker.stub()
  472. stub("foo")
  473. stub.assert_called_once_with("foo")
  474. stub("foo")
  475. with assert_traceback():
  476. stub.assert_called_once_with("foo")
  477. def test_assert_called_once_wrapper(mocker: MockerFixture) -> None:
  478. stub = mocker.stub()
  479. if not hasattr(stub, "assert_called_once"):
  480. pytest.skip("assert_called_once not available")
  481. stub("foo")
  482. stub.assert_called_once()
  483. stub("foo")
  484. with assert_traceback():
  485. stub.assert_called_once()
  486. def test_assert_called_wrapper(mocker: MockerFixture) -> None:
  487. stub = mocker.stub()
  488. if not hasattr(stub, "assert_called"):
  489. pytest.skip("assert_called_once not available")
  490. with assert_traceback():
  491. stub.assert_called()
  492. stub("foo")
  493. stub.assert_called()
  494. stub("foo")
  495. stub.assert_called()
  496. @pytest.mark.skip
  497. @pytest.mark.usefixtures("needs_assert_rewrite")
  498. def test_assert_called_args_with_introspection(mocker: MockerFixture) -> None:
  499. stub = mocker.stub()
  500. complex_args = ("a", 1, {"test"})
  501. wrong_args = ("b", 2, {"jest"})
  502. stub(*complex_args)
  503. stub.assert_called_with(*complex_args)
  504. stub.assert_called_once_with(*complex_args)
  505. with assert_argument_introspection(complex_args, wrong_args):
  506. stub.assert_called_with(*wrong_args)
  507. stub.assert_called_once_with(*wrong_args)
  508. @pytest.mark.skip
  509. @pytest.mark.usefixtures("needs_assert_rewrite")
  510. def test_assert_called_kwargs_with_introspection(mocker: MockerFixture) -> None:
  511. stub = mocker.stub()
  512. complex_kwargs = dict(foo={"bar": 1, "baz": "spam"})
  513. wrong_kwargs = dict(foo={"goo": 1, "baz": "bran"})
  514. stub(**complex_kwargs)
  515. stub.assert_called_with(**complex_kwargs)
  516. stub.assert_called_once_with(**complex_kwargs)
  517. with assert_argument_introspection(complex_kwargs, wrong_kwargs):
  518. stub.assert_called_with(**wrong_kwargs)
  519. stub.assert_called_once_with(**wrong_kwargs)
  520. def test_assert_any_call_wrapper(mocker: MockerFixture) -> None:
  521. stub = mocker.stub()
  522. stub("foo")
  523. stub("foo")
  524. stub.assert_any_call("foo")
  525. with assert_traceback():
  526. stub.assert_any_call("bar")
  527. def test_assert_has_calls(mocker: MockerFixture) -> None:
  528. stub = mocker.stub()
  529. stub("foo")
  530. stub.assert_has_calls([mocker.call("foo")])
  531. with assert_traceback():
  532. stub.assert_has_calls([mocker.call("bar")])
  533. def test_assert_has_calls_multiple_calls(mocker: MockerFixture) -> None:
  534. stub = mocker.stub()
  535. stub("foo")
  536. stub("bar")
  537. stub("baz")
  538. stub.assert_has_calls([mocker.call("foo"), mocker.call("bar"), mocker.call("baz")])
  539. with assert_traceback():
  540. stub.assert_has_calls(
  541. [
  542. mocker.call("foo"),
  543. mocker.call("bar"),
  544. mocker.call("baz"),
  545. mocker.call("bat"),
  546. ]
  547. )
  548. with assert_traceback():
  549. stub.assert_has_calls(
  550. [mocker.call("foo"), mocker.call("baz"), mocker.call("bar")]
  551. )
  552. def test_assert_has_calls_multiple_calls_subset(mocker: MockerFixture) -> None:
  553. stub = mocker.stub()
  554. stub("foo")
  555. stub("bar")
  556. stub("baz")
  557. stub.assert_has_calls([mocker.call("bar"), mocker.call("baz")])
  558. with assert_traceback():
  559. stub.assert_has_calls([mocker.call("foo"), mocker.call("baz")])
  560. with assert_traceback():
  561. stub.assert_has_calls(
  562. [mocker.call("foo"), mocker.call("bar"), mocker.call("bat")]
  563. )
  564. with assert_traceback():
  565. stub.assert_has_calls([mocker.call("baz"), mocker.call("bar")])
  566. def test_assert_has_calls_multiple_calls_any_order(mocker: MockerFixture) -> None:
  567. stub = mocker.stub()
  568. stub("foo")
  569. stub("bar")
  570. stub("baz")
  571. stub.assert_has_calls(
  572. [mocker.call("foo"), mocker.call("baz"), mocker.call("bar")], any_order=True
  573. )
  574. with assert_traceback():
  575. stub.assert_has_calls(
  576. [
  577. mocker.call("foo"),
  578. mocker.call("baz"),
  579. mocker.call("bar"),
  580. mocker.call("bat"),
  581. ],
  582. any_order=True,
  583. )
  584. def test_assert_has_calls_multiple_calls_any_order_subset(
  585. mocker: MockerFixture,
  586. ) -> None:
  587. stub = mocker.stub()
  588. stub("foo")
  589. stub("bar")
  590. stub("baz")
  591. stub.assert_has_calls([mocker.call("baz"), mocker.call("foo")], any_order=True)
  592. with assert_traceback():
  593. stub.assert_has_calls(
  594. [mocker.call("baz"), mocker.call("foo"), mocker.call("bat")], any_order=True
  595. )
  596. def test_assert_has_calls_no_calls(
  597. mocker: MockerFixture,
  598. ) -> None:
  599. stub = mocker.stub()
  600. stub.assert_has_calls([])
  601. with assert_traceback():
  602. stub.assert_has_calls([mocker.call("foo")])
  603. @pytest.mark.skip("Skip testdir")
  604. def test_monkeypatch_ini(testdir: Any, mocker: MockerFixture) -> None:
  605. # Make sure the following function actually tests something
  606. stub = mocker.stub()
  607. assert stub.assert_called_with.__module__ != stub.__module__
  608. testdir.makepyfile(
  609. """
  610. def test_foo(mocker):
  611. stub = mocker.stub()
  612. assert stub.assert_called_with.__module__ == stub.__module__
  613. """
  614. )
  615. testdir.makeini(
  616. """
  617. [pytest]
  618. mock_traceback_monkeypatch = false
  619. """
  620. )
  621. result = testdir.runpytest_subprocess()
  622. assert result.ret == 0
  623. def test_parse_ini_boolean() -> None:
  624. from pytest_mock._util import parse_ini_boolean
  625. assert parse_ini_boolean("True") is True
  626. assert parse_ini_boolean("false") is False
  627. with pytest.raises(ValueError):
  628. parse_ini_boolean("foo")
  629. def test_patched_method_parameter_name(mocker: MockerFixture) -> None:
  630. """Test that our internal code uses uncommon names when wrapping other
  631. "mock" methods to avoid conflicts with user code (#31).
  632. """
  633. class Request:
  634. @classmethod
  635. def request(cls, method, args):
  636. pass
  637. m = mocker.patch.object(Request, "request")
  638. Request.request(method="get", args={"type": "application/json"})
  639. m.assert_called_once_with(method="get", args={"type": "application/json"})
  640. @pytest.mark.skip("Skip testdir")
  641. def test_monkeypatch_native(testdir: Any) -> None:
  642. """Automatically disable monkeypatching when --tb=native."""
  643. testdir.makepyfile(
  644. """
  645. def test_foo(mocker):
  646. stub = mocker.stub()
  647. stub(1, greet='hello')
  648. stub.assert_called_once_with(1, greet='hey')
  649. """
  650. )
  651. result = testdir.runpytest_subprocess("--tb=native")
  652. assert result.ret == 1
  653. assert "During handling of the above exception" not in result.stdout.str()
  654. assert "Differing items:" not in result.stdout.str()
  655. traceback_lines = [
  656. x
  657. for x in result.stdout.str().splitlines()
  658. if "Traceback (most recent call last)" in x
  659. ]
  660. assert (
  661. len(traceback_lines) == 1
  662. ) # make sure there are no duplicated tracebacks (#44)
  663. @pytest.mark.skip("Skip testdir")
  664. def test_monkeypatch_no_terminal(testdir: Any) -> None:
  665. """Don't crash without 'terminal' plugin."""
  666. testdir.makepyfile(
  667. """
  668. def test_foo(mocker):
  669. stub = mocker.stub()
  670. stub(1, greet='hello')
  671. stub.assert_called_once_with(1, greet='hey')
  672. """
  673. )
  674. result = testdir.runpytest_subprocess("-p", "no:terminal", "-s")
  675. assert result.ret == 1
  676. assert result.stdout.lines == []
  677. @pytest.mark.skip("Skip testdir")
  678. def test_standalone_mock(testdir: Any) -> None:
  679. """Check that the "mock_use_standalone" is being used."""
  680. pytest.importorskip("mock")
  681. testdir.makepyfile(
  682. """
  683. import mock
  684. def test_foo(mocker):
  685. assert mock.MagicMock is mocker.MagicMock
  686. """
  687. )
  688. testdir.makeini(
  689. """
  690. [pytest]
  691. mock_use_standalone_module = true
  692. """
  693. )
  694. result = testdir.runpytest_subprocess()
  695. assert result.ret == 0
  696. @pytest.mark.skip("Skip testdir")
  697. @pytest.mark.usefixtures("needs_assert_rewrite")
  698. def test_detailed_introspection(testdir: Any) -> None:
  699. """Check that the "mock_use_standalone" is being used."""
  700. testdir.makeini(
  701. """
  702. [pytest]
  703. asyncio_mode=auto
  704. """
  705. )
  706. testdir.makepyfile(
  707. """
  708. def test(mocker):
  709. m = mocker.Mock()
  710. m('fo')
  711. m.assert_called_once_with('', bar=4)
  712. """
  713. )
  714. result = testdir.runpytest("-s")
  715. expected_lines = [
  716. "*AssertionError: expected call not found.",
  717. "*Expected: mock('', bar=4)",
  718. "*Actual: mock('fo')",
  719. ]
  720. expected_lines += [
  721. "*pytest introspection follows:*",
  722. "*Args:",
  723. "*assert ('fo',) == ('',)",
  724. "*At index 0 diff: 'fo' != ''*",
  725. "*Use -v to*",
  726. "*Kwargs:*",
  727. "*assert {} == {'bar': 4}*",
  728. "*Right contains* more item*",
  729. "*{'bar': 4}*",
  730. "*Use -v to*",
  731. ]
  732. result.stdout.fnmatch_lines(expected_lines)
  733. @pytest.mark.skip("Skip testdir")
  734. @pytest.mark.usefixtures("needs_assert_rewrite")
  735. def test_detailed_introspection_async(testdir: Any) -> None:
  736. """Check that the "mock_use_standalone" is being used."""
  737. testdir.makeini(
  738. """
  739. [pytest]
  740. asyncio_mode=auto
  741. """
  742. )
  743. testdir.makepyfile(
  744. """
  745. import pytest
  746. async def test(mocker):
  747. m = mocker.AsyncMock()
  748. await m('fo')
  749. m.assert_awaited_once_with('', bar=4)
  750. """
  751. )
  752. result = testdir.runpytest("-s")
  753. expected_lines = [
  754. "*AssertionError: expected await not found.",
  755. "*Expected: mock('', bar=4)",
  756. "*Actual: mock('fo')",
  757. "*pytest introspection follows:*",
  758. "*Args:",
  759. "*assert ('fo',) == ('',)",
  760. "*At index 0 diff: 'fo' != ''*",
  761. "*Use -v to*",
  762. "*Kwargs:*",
  763. "*assert {} == {'bar': 4}*",
  764. "*Right contains* more item*",
  765. "*{'bar': 4}*",
  766. "*Use -v to*",
  767. ]
  768. result.stdout.fnmatch_lines(expected_lines)
  769. @pytest.mark.skip("Skip testdir")
  770. def test_missing_introspection(testdir: Any) -> None:
  771. testdir.makepyfile(
  772. """
  773. def test_foo(mocker):
  774. mock = mocker.Mock()
  775. mock('foo')
  776. mock('test')
  777. mock.assert_called_once_with('test')
  778. """
  779. )
  780. result = testdir.runpytest()
  781. assert "pytest introspection follows:" not in result.stdout.str()
  782. def test_assert_called_with_unicode_arguments(mocker: MockerFixture) -> None:
  783. """Test bug in assert_call_with called with non-ascii unicode string (#91)"""
  784. stub = mocker.stub()
  785. stub(b"l\xc3\xb6k".decode("UTF-8"))
  786. with pytest.raises(AssertionError):
  787. stub.assert_called_with("lak")
  788. @pytest.mark.skip("Skip testdir")
  789. def test_plain_stopall(testdir: Any) -> None:
  790. """patch.stopall() in a test should not cause an error during unconfigure (#137)"""
  791. testdir.makeini(
  792. """
  793. [pytest]
  794. asyncio_mode=auto
  795. """
  796. )
  797. testdir.makepyfile(
  798. """
  799. import random
  800. def get_random_number():
  801. return random.randint(0, 100)
  802. def test_get_random_number(mocker):
  803. patcher = mocker.mock_module.patch("random.randint", lambda x, y: 5)
  804. patcher.start()
  805. assert get_random_number() == 5
  806. mocker.mock_module.patch.stopall()
  807. """
  808. )
  809. result = testdir.runpytest_subprocess()
  810. result.stdout.fnmatch_lines("* 1 passed in *")
  811. assert "RuntimeError" not in result.stderr.str()
  812. def test_warn_patch_object_context_manager(mocker: MockerFixture) -> None:
  813. class A:
  814. def doIt(self):
  815. return False
  816. a = A()
  817. expected_warning_msg = (
  818. "Mocks returned by pytest-mock do not need to be used as context managers. "
  819. "The mocker fixture automatically undoes mocking at the end of a test. "
  820. "This warning can be ignored if it was triggered by mocking a context manager. "
  821. "https://pytest-mock.readthedocs.io/en/latest/remarks.html#usage-as-context-manager"
  822. )
  823. with pytest.warns(
  824. PytestMockWarning, match=re.escape(expected_warning_msg)
  825. ) as warn_record:
  826. with mocker.patch.object(a, "doIt", return_value=True):
  827. assert a.doIt() is True
  828. assert warn_record[0].filename == __file__
  829. def test_warn_patch_context_manager(mocker: MockerFixture) -> None:
  830. expected_warning_msg = (
  831. "Mocks returned by pytest-mock do not need to be used as context managers. "
  832. "The mocker fixture automatically undoes mocking at the end of a test. "
  833. "This warning can be ignored if it was triggered by mocking a context manager. "
  834. "https://pytest-mock.readthedocs.io/en/latest/remarks.html#usage-as-context-manager"
  835. )
  836. with pytest.warns(
  837. PytestMockWarning, match=re.escape(expected_warning_msg)
  838. ) as warn_record:
  839. with mocker.patch("json.loads"):
  840. pass
  841. assert warn_record[0].filename == __file__
  842. def test_context_manager_patch_example(mocker: MockerFixture) -> None:
  843. """Our message about misusing mocker as a context manager should not affect mocking
  844. context managers (see #192)"""
  845. class dummy_module:
  846. class MyContext:
  847. def __enter__(self, *args, **kwargs):
  848. return 10
  849. def __exit__(self, *args, **kwargs):
  850. pass
  851. def my_func():
  852. with dummy_module.MyContext() as v:
  853. return v
  854. mocker.patch.object(dummy_module, "MyContext")
  855. assert isinstance(my_func(), mocker.MagicMock)
  856. def test_patch_context_manager_with_context_manager(mocker: MockerFixture) -> None:
  857. """Test that no warnings are issued when an object patched with
  858. patch.context_manager is used as a context manager (#221)"""
  859. class A:
  860. def doIt(self):
  861. return False
  862. a = A()
  863. with warnings.catch_warnings(record=True) as warn_record:
  864. with mocker.patch.context_manager(a, "doIt", return_value=True):
  865. assert a.doIt() is True
  866. assert len(warn_record) == 0
  867. @pytest.mark.skip("Skip testdir")
  868. def test_abort_patch_context_manager_with_stale_pyc(testdir: Any) -> None:
  869. """Ensure we don't trigger an error in case the frame where mocker.patch is being
  870. used doesn't have a 'context' (#169)"""
  871. import compileall
  872. py_fn = testdir.makepyfile(
  873. c="""
  874. class C:
  875. x = 1
  876. def check(mocker):
  877. mocker.patch.object(C, "x", 2)
  878. assert C.x == 2
  879. """
  880. )
  881. testdir.syspathinsert()
  882. testdir.makepyfile(
  883. """
  884. from c import check
  885. def test_foo(mocker):
  886. check(mocker)
  887. """
  888. )
  889. result = testdir.runpytest()
  890. result.assert_outcomes(passed=1)
  891. assert compileall.compile_file(str(py_fn), legacy=True)
  892. pyc_fn = str(py_fn) + "c"
  893. assert os.path.isfile(pyc_fn)
  894. py_fn.remove()
  895. result = testdir.runpytest()
  896. result.assert_outcomes(passed=1)
  897. @pytest.mark.skip("Skip testdir")
  898. def test_used_with_class_scope(testdir: Any) -> None:
  899. testdir.makeini(
  900. """
  901. [pytest]
  902. asyncio_mode=auto
  903. """
  904. )
  905. testdir.makepyfile(
  906. """
  907. import pytest
  908. import random
  909. import unittest
  910. def get_random_number():
  911. return random.randint(0, 1)
  912. @pytest.fixture(autouse=True, scope="class")
  913. def randint_mock(class_mocker):
  914. return class_mocker.patch("random.randint", lambda x, y: 5)
  915. class TestGetRandomNumber(unittest.TestCase):
  916. def test_get_random_number(self):
  917. assert get_random_number() == 5
  918. """
  919. )
  920. result = testdir.runpytest_subprocess()
  921. assert "AssertionError" not in result.stderr.str()
  922. result.stdout.fnmatch_lines("* 1 passed in *")
  923. @pytest.mark.skip("Skip testdir")
  924. def test_used_with_module_scope(testdir: Any) -> None:
  925. testdir.makeini(
  926. """
  927. [pytest]
  928. asyncio_mode=auto
  929. """
  930. )
  931. testdir.makepyfile(
  932. """
  933. import pytest
  934. import random
  935. def get_random_number():
  936. return random.randint(0, 1)
  937. @pytest.fixture(autouse=True, scope="module")
  938. def randint_mock(module_mocker):
  939. return module_mocker.patch("random.randint", lambda x, y: 5)
  940. def test_get_random_number():
  941. assert get_random_number() == 5
  942. """
  943. )
  944. result = testdir.runpytest_subprocess()
  945. assert "AssertionError" not in result.stderr.str()
  946. result.stdout.fnmatch_lines("* 1 passed in *")
  947. @pytest.mark.skip("Skip testdir")
  948. def test_used_with_package_scope(testdir: Any) -> None:
  949. testdir.makeini(
  950. """
  951. [pytest]
  952. asyncio_mode=auto
  953. """
  954. )
  955. testdir.makepyfile(
  956. """
  957. import pytest
  958. import random
  959. def get_random_number():
  960. return random.randint(0, 1)
  961. @pytest.fixture(autouse=True, scope="package")
  962. def randint_mock(package_mocker):
  963. return package_mocker.patch("random.randint", lambda x, y: 5)
  964. def test_get_random_number():
  965. assert get_random_number() == 5
  966. """
  967. )
  968. result = testdir.runpytest_subprocess()
  969. assert "AssertionError" not in result.stderr.str()
  970. result.stdout.fnmatch_lines("* 1 passed in *")
  971. @pytest.mark.skip("Skip testdir")
  972. def test_used_with_session_scope(testdir: Any) -> None:
  973. testdir.makeini(
  974. """
  975. [pytest]
  976. asyncio_mode=auto
  977. """
  978. )
  979. testdir.makepyfile(
  980. """
  981. import pytest
  982. import random
  983. def get_random_number():
  984. return random.randint(0, 1)
  985. @pytest.fixture(autouse=True, scope="session")
  986. def randint_mock(session_mocker):
  987. return session_mocker.patch("random.randint", lambda x, y: 5)
  988. def test_get_random_number():
  989. assert get_random_number() == 5
  990. """
  991. )
  992. result = testdir.runpytest_subprocess()
  993. assert "AssertionError" not in result.stderr.str()
  994. result.stdout.fnmatch_lines("* 1 passed in *")
  995. def test_stop_patch(mocker):
  996. class UnSpy:
  997. def foo(self):
  998. return 42
  999. m = mocker.patch.object(UnSpy, "foo", return_value=0)
  1000. assert UnSpy().foo() == 0
  1001. mocker.stop(m)
  1002. assert UnSpy().foo() == 42
  1003. with pytest.raises(ValueError):
  1004. mocker.stop(m)
  1005. def test_stop_instance_patch(mocker):
  1006. class UnSpy:
  1007. def foo(self):
  1008. return 42
  1009. m = mocker.patch.object(UnSpy, "foo", return_value=0)
  1010. un_spy = UnSpy()
  1011. assert un_spy.foo() == 0
  1012. mocker.stop(m)
  1013. assert un_spy.foo() == 42
  1014. def test_stop_spy(mocker):
  1015. class UnSpy:
  1016. def foo(self):
  1017. return 42
  1018. spy = mocker.spy(UnSpy, "foo")
  1019. assert UnSpy().foo() == 42
  1020. assert spy.call_count == 1
  1021. mocker.stop(spy)
  1022. assert UnSpy().foo() == 42
  1023. assert spy.call_count == 1
  1024. def test_stop_instance_spy(mocker):
  1025. class UnSpy:
  1026. def foo(self):
  1027. return 42
  1028. spy = mocker.spy(UnSpy, "foo")
  1029. un_spy = UnSpy()
  1030. assert un_spy.foo() == 42
  1031. assert spy.call_count == 1
  1032. mocker.stop(spy)
  1033. assert un_spy.foo() == 42
  1034. assert spy.call_count == 1
  1035. def test_stop_multiple_patches(mocker: MockerFixture) -> None:
  1036. """Regression for #420."""
  1037. class Class1:
  1038. @staticmethod
  1039. def get():
  1040. return 1
  1041. class Class2:
  1042. @staticmethod
  1043. def get():
  1044. return 2
  1045. def handle_get():
  1046. return 3
  1047. mocker.patch.object(Class1, "get", handle_get)
  1048. mocker.patch.object(Class2, "get", handle_get)
  1049. mocker.stopall()
  1050. assert Class1.get() == 1
  1051. assert Class2.get() == 2