由于 pytest 中的 Mocking 导致的 INTERNALERROR 覆盖率
coverage INTERNALERROR due to Mocking in pytest
我正在尝试测试以下功能:
def my_testable_function(input_filename):
if os.path.isfile(input_filename):
return "Exist!"
return "Not exist"
这是我第一次测试上面的功能:
def test_1():
os.path.isfile = Mock(return_value=True)
result = my_testable_function("/existing/path/file.txt")
assert result == "Exist!"
运行 pytest==6.2.1 + pytest-cov==2.11.1:
$ py.test -v --ff --cov=<my_package> tests
我最终得到了 INTERNALERROR:
====================================================================== test session starts =======================================================================
platform darwin -- Python 3.8.5, pytest-6.2.1, py-1.10.0, pluggy-0.13.1 -- /Users/my-user/my-project/venv/bin/python3
cachedir: .pytest_cache
rootdir: /Users/my-user/my-project
plugins: cov-2.11.1, aiohttp-0.3.0
collected 6 items
run-last-failure: no previously failed tests, not deselecting items.
tests/test_app.py::test_1 PASSED [100%]
INTERNALERROR> Traceback (most recent call last):
INTERNALERROR> File "/Users/my-user/my-project/venv/lib/python3.8/site-packages/_pytest/main.py", line 269, in wrap_session
INTERNALERROR> session.exitstatus = doit(config, session) or 0
INTERNALERROR> File "/Users/my-user/my-project/venv/lib/python3.8/site-packages/_pytest/main.py", line 323, in _main
INTERNALERROR> config.hook.pytest_runtestloop(session=session)
INTERNALERROR> File "/Users/my-user/my-project/venv/lib/python3.8/site-packages/pluggy/hooks.py", line 286, in __call__
INTERNALERROR> return self._hookexec(self, self.get_hookimpls(), kwargs)
INTERNALERROR> File "/Users/my-user/my-project/venv/lib/python3.8/site-packages/pluggy/manager.py", line 93, in _hookexec
INTERNALERROR> return self._inner_hookexec(hook, methods, kwargs)
INTERNALERROR> File "/Users/my-user/my-project/venv/lib/python3.8/site-packages/pluggy/manager.py", line 84, in <lambda>
INTERNALERROR> self._inner_hookexec = lambda hook, methods, kwargs: hook.multicall(
INTERNALERROR> File "/Users/my-user/my-project/venv/lib/python3.8/site-packages/pluggy/callers.py", line 203, in _multicall
INTERNALERROR> gen.send(outcome)
INTERNALERROR> File "/Users/my-user/my-project/venv/lib/python3.8/site-packages/pytest_cov/plugin.py", line 271, in pytest_runtestloop
INTERNALERROR> self.cov_controller.finish()
INTERNALERROR> File "/Users/my-user/my-project/venv/lib/python3.8/site-packages/pytest_cov/engine.py", line 44, in ensure_topdir_wrapper
INTERNALERROR> return meth(self, *args, **kwargs)
INTERNALERROR> File "/Users/my-user/my-project/venv/lib/python3.8/site-packages/pytest_cov/engine.py", line 230, in finish
INTERNALERROR> self.cov.stop()
INTERNALERROR> File "/Users/my-user/my-project/venv/lib/python3.8/site-packages/coverage/control.py", line 701, in combine
INTERNALERROR> combine_parallel_data(
INTERNALERROR> File "/Users/my-user/my-project/venv/lib/python3.8/site-packages/coverage/data.py", line 110, in combine_parallel_data
INTERNALERROR> new_data.read()
INTERNALERROR> File "/Users/my-user/my-project/venv/lib/python3.8/site-packages/coverage/sqldata.py", line 753, in read
INTERNALERROR> with self._connect(): # TODO: doesn't look right
INTERNALERROR> File "/Users/my-user/my-project/venv/lib/python3.8/site-packages/coverage/sqldata.py", line 298, in _connect
INTERNALERROR> self._open_db()
INTERNALERROR> File "/Users/my-user/my-project/venv/lib/python3.8/site-packages/coverage/sqldata.py", line 266, in _open_db
INTERNALERROR> self._read_db()
INTERNALERROR> File "/Users/my-user/my-project/venv/lib/python3.8/site-packages/coverage/sqldata.py", line 270, in _read_db
INTERNALERROR> with self._dbs[get_thread_id()] as db:
INTERNALERROR> File "/Users/my-user/my-project/venv/lib/python3.8/site-packages/coverage/sqldata.py", line 1037, in __enter__
INTERNALERROR> self._connect()
INTERNALERROR> File "/Users/my-user/my-project/venv/lib/python3.8/site-packages/coverage/sqldata.py", line 1019, in _connect
INTERNALERROR> self.con = sqlite3.connect(filename, check_same_thread=False)
INTERNALERROR> sqlite3.OperationalError: unable to open database file
尝试用 monkeypatch 来做(没有 INTERNALERROR
):
def mock_is_file(filename):
if filename == "/existing/path/file.txt":
return True
return os.path.isfile(filename)
def test_2(monkeypatch):
monkeypatch.setattr(os.path, 'isfile', mock_is_file)
my_testable_function("/existing/path/file.txt")
assert result == "Exist!"
我的问题是,为什么在我第一次尝试时会发生这种情况,为什么在第二次尝试时会起作用?
当你放置模拟时,你还必须确保在测试结束时撤消模拟。您在测试中更改了 os.path.isfile,并保留更改。后来,coverage 需要那个函数,取而代之的是你的 mock。
monkeypatch 方法之所以有效,是因为 monkeypatch fixture 会在测试完成后自动清理 mock。
我正在尝试测试以下功能:
def my_testable_function(input_filename):
if os.path.isfile(input_filename):
return "Exist!"
return "Not exist"
这是我第一次测试上面的功能:
def test_1():
os.path.isfile = Mock(return_value=True)
result = my_testable_function("/existing/path/file.txt")
assert result == "Exist!"
运行 pytest==6.2.1 + pytest-cov==2.11.1:
$ py.test -v --ff --cov=<my_package> tests
我最终得到了 INTERNALERROR:
====================================================================== test session starts =======================================================================
platform darwin -- Python 3.8.5, pytest-6.2.1, py-1.10.0, pluggy-0.13.1 -- /Users/my-user/my-project/venv/bin/python3
cachedir: .pytest_cache
rootdir: /Users/my-user/my-project
plugins: cov-2.11.1, aiohttp-0.3.0
collected 6 items
run-last-failure: no previously failed tests, not deselecting items.
tests/test_app.py::test_1 PASSED [100%]
INTERNALERROR> Traceback (most recent call last):
INTERNALERROR> File "/Users/my-user/my-project/venv/lib/python3.8/site-packages/_pytest/main.py", line 269, in wrap_session
INTERNALERROR> session.exitstatus = doit(config, session) or 0
INTERNALERROR> File "/Users/my-user/my-project/venv/lib/python3.8/site-packages/_pytest/main.py", line 323, in _main
INTERNALERROR> config.hook.pytest_runtestloop(session=session)
INTERNALERROR> File "/Users/my-user/my-project/venv/lib/python3.8/site-packages/pluggy/hooks.py", line 286, in __call__
INTERNALERROR> return self._hookexec(self, self.get_hookimpls(), kwargs)
INTERNALERROR> File "/Users/my-user/my-project/venv/lib/python3.8/site-packages/pluggy/manager.py", line 93, in _hookexec
INTERNALERROR> return self._inner_hookexec(hook, methods, kwargs)
INTERNALERROR> File "/Users/my-user/my-project/venv/lib/python3.8/site-packages/pluggy/manager.py", line 84, in <lambda>
INTERNALERROR> self._inner_hookexec = lambda hook, methods, kwargs: hook.multicall(
INTERNALERROR> File "/Users/my-user/my-project/venv/lib/python3.8/site-packages/pluggy/callers.py", line 203, in _multicall
INTERNALERROR> gen.send(outcome)
INTERNALERROR> File "/Users/my-user/my-project/venv/lib/python3.8/site-packages/pytest_cov/plugin.py", line 271, in pytest_runtestloop
INTERNALERROR> self.cov_controller.finish()
INTERNALERROR> File "/Users/my-user/my-project/venv/lib/python3.8/site-packages/pytest_cov/engine.py", line 44, in ensure_topdir_wrapper
INTERNALERROR> return meth(self, *args, **kwargs)
INTERNALERROR> File "/Users/my-user/my-project/venv/lib/python3.8/site-packages/pytest_cov/engine.py", line 230, in finish
INTERNALERROR> self.cov.stop()
INTERNALERROR> File "/Users/my-user/my-project/venv/lib/python3.8/site-packages/coverage/control.py", line 701, in combine
INTERNALERROR> combine_parallel_data(
INTERNALERROR> File "/Users/my-user/my-project/venv/lib/python3.8/site-packages/coverage/data.py", line 110, in combine_parallel_data
INTERNALERROR> new_data.read()
INTERNALERROR> File "/Users/my-user/my-project/venv/lib/python3.8/site-packages/coverage/sqldata.py", line 753, in read
INTERNALERROR> with self._connect(): # TODO: doesn't look right
INTERNALERROR> File "/Users/my-user/my-project/venv/lib/python3.8/site-packages/coverage/sqldata.py", line 298, in _connect
INTERNALERROR> self._open_db()
INTERNALERROR> File "/Users/my-user/my-project/venv/lib/python3.8/site-packages/coverage/sqldata.py", line 266, in _open_db
INTERNALERROR> self._read_db()
INTERNALERROR> File "/Users/my-user/my-project/venv/lib/python3.8/site-packages/coverage/sqldata.py", line 270, in _read_db
INTERNALERROR> with self._dbs[get_thread_id()] as db:
INTERNALERROR> File "/Users/my-user/my-project/venv/lib/python3.8/site-packages/coverage/sqldata.py", line 1037, in __enter__
INTERNALERROR> self._connect()
INTERNALERROR> File "/Users/my-user/my-project/venv/lib/python3.8/site-packages/coverage/sqldata.py", line 1019, in _connect
INTERNALERROR> self.con = sqlite3.connect(filename, check_same_thread=False)
INTERNALERROR> sqlite3.OperationalError: unable to open database file
尝试用 monkeypatch 来做(没有 INTERNALERROR
):
def mock_is_file(filename):
if filename == "/existing/path/file.txt":
return True
return os.path.isfile(filename)
def test_2(monkeypatch):
monkeypatch.setattr(os.path, 'isfile', mock_is_file)
my_testable_function("/existing/path/file.txt")
assert result == "Exist!"
我的问题是,为什么在我第一次尝试时会发生这种情况,为什么在第二次尝试时会起作用?
当你放置模拟时,你还必须确保在测试结束时撤消模拟。您在测试中更改了 os.path.isfile,并保留更改。后来,coverage 需要那个函数,取而代之的是你的 mock。
monkeypatch 方法之所以有效,是因为 monkeypatch fixture 会在测试完成后自动清理 mock。