Jos*_*eak 5 python pytest python-3.x
我已经开始使用pytest-vcrwhich 是一个pytest插件包装VCR.py,我在这篇关于 Advanced Python Testing 的博客文章中记录了它。
它cassettes/*.yml在第一次测试运行时将所有 HTTP 流量记录到文件中以保存快照。类似于Web 组件的Jest快照测试。
在后续的测试运行中,如果请求格式错误,它将找不到匹配项并抛出异常,表示禁止记录新请求并且未找到现有记录。
VCR.py引发 aCannotOverwriteExistingCassetteException并没有特别说明为什么它不匹配。
我如何利用 pytest
pytest_exception_interact钩子将这个异常替换为一个利用夹具信息的信息更丰富的异常?
我一头扎进我的site-packages地方VCR.py是pip installed并重新编写我希望如何处理异常。我只需要知道如何让这个pytest_exception_interact钩子正常工作,以从该测试节点访问设备(在它被清理之前)并引发不同的异常。
让我们获取依赖项。
$ pip install pytest pytest-vcr requests
Run Code Online (Sandbox Code Playgroud)
test_example.py:
import pytest
import requests
@pytest.mark.vcr
def test_example():
r = requests.get("https://www.stackoverflow.com")
assert r.status_code == 200
Run Code Online (Sandbox Code Playgroud)
$ pytest test_example.py --vcr-record=once
...
test_example.py::test_example PASSED
...
$ ls cassettes/
cassettes/test_example.yml
$ head cassettes/test_example.yml
interactions:
- request:
uri: https://wwwstackoverflow.com
body: null
headers:
Accept:
- '*/*'
$ pytest test_example.py --vcr-record=none
...
test_example.py::test_example PASSED
...
Run Code Online (Sandbox Code Playgroud)
现在将测试中的 URI 更改为“ https://www.google.com ”:
test_example.py:
import pytest
import requests
@pytest.mark.vcr
def test_example():
r = requests.get("https://www.google.com")
assert r.status_code == 200
Run Code Online (Sandbox Code Playgroud)
并再次运行测试以检测回归:
$ pytest test_example.py --vcr-record=none
E vcr.errors.CannotOverwriteExistingCassetteException: No match for the request (<Request (GET) https://www.google.com/>)
...
Run Code Online (Sandbox Code Playgroud)
我可以将一个conftest.py文件添加到我的测试结构的根目录以创建一个本地插件,并且我可以验证我可以拦截异常并使用以下方法注入我自己的:
conftest.py
import pytest
from vcr.errors import CannotOverwriteExistingCassetteException
from vcr.config import VCR
from vcr.cassette import Cassette
class RequestNotFoundCassetteException(CannotOverwriteExistingCassetteException):
...
@pytest.fixture(autouse=True)
def _vcr_marker(request):
marker = request.node.get_closest_marker("vcr")
if marker:
cassette = request.getfixturevalue("vcr_cassette")
vcr = request.getfixturevalue("vcr")
request.node.__vcr_fixtures = dict(vcr_cassette=cassette, vcr=vcr)
yield
@pytest.hookimpl(hookwrapper=True)
def pytest_exception_interact(node, call, report):
excinfo = call.excinfo
if report.when == "call" and isinstance(excinfo.value, CannotOverwriteExistingCassetteException):
# Safely check for fixture pass through on this node
cassette = None
vcr = None
if hasattr(node, "__vcr_fixtures"):
for fixture_name, fx in node.__vcr_fixtures.items():
vcr = fx if isinstance(fx, VCR)
cassette = fx if isinstance(fx, Cassette)
# If we have the extra fixture context available...
if cassette and vcr:
match_properties = [f.__name__ for f in cassette._match_on]
cassette_reqs = cassette.requests
# filtered_req = cassette.filter_request(vcr._vcr_request)
# this_req, req_str = __format_near_match(filtered_req, cassette_reqs, match_properties)
# Raise and catch a new excpetion FROM existing one to keep the traceback
# https://stackoverflow.com/a/24752607/622276
# https://docs.python.org/3/library/exceptions.html#built-in-exceptions
try:
raise RequestNotFoundCassetteException(
f"\nMatching Properties: {match_properties}\n" f"Cassette Requests: {cassette_reqs}\n"
) from excinfo.value
except RequestNotFoundCassetteException as e:
excinfo._excinfo = (type(e), e)
report.longrepr = node.repr_failure(excinfo)
Run Code Online (Sandbox Code Playgroud)
这是互联网上的文档变得非常薄弱的部分。
如何访问
vcr_cassette夹具并返回不同的异常?
我想要做的是获取filtered_request试图被请求的信息,cassette_requests以及使用Python difflib标准库的列表生成针对不同信息的增量。
The internals of running a single test with pytest triggers pytest_runtest_protocol which effectively runs the following three call_and_report calls to get a collection of reports.
def runtestprotocol(item, log=True, nextitem=None):
# Abbreviated
reports = []
reports.append(call_and_report(item, "setup", log))
reports.append(call_and_report(item, "call", log))
reports.append(call_and_report(item, "teardown", log))
return reports
Run Code Online (Sandbox Code Playgroud)
So I'm after modifying the report at the call stage... but still no clue how I get access to the fixture information.
src/_pytest/runner.py:L166-L174
def call_and_report(item, when, log=True, **kwds):
call = call_runtest_hook(item, when, **kwds)
hook = item.ihook
report = hook.pytest_runtest_makereport(item=item, call=call)
if log:
hook.pytest_runtest_logreport(report=report)
if check_interactive_exception(call, report):
hook.pytest_exception_interact(node=item, call=call, report=report)
return report
Run Code Online (Sandbox Code Playgroud)
It looks like there are some helper methods for generating a new ExceptionRepresentation so I updated the conftest.py example.
longrepr = item.repr_failure(excinfo)
Run Code Online (Sandbox Code Playgroud)
UPDATE #1 2019-06-26: Thanks to some pointers from @hoefling in the comments I updated my conftest.py.
raise ... from ... form._vcr_marker to attach the vcr and vcr_cassette fixtures to the request.node which represent that individual test item.UPDATE #2 2019-06-26
It would seem impossible to get at the VCRHTTPConnections that were patched in creating the cassette context manager. I have opened up the following pull request to pass as arguments when the exception is thrown, to then catch and handle arbitrarily down stream.
https://github.com/kevin1024/vcrpy/pull/445
Related questions that are informative but still don't answer this question.
感谢@hoefling评论中的评论和指导。
我可以将cassette夹具附加到覆盖标记的本地插件request.node中......conftest.pypytest-vcr
@pytest.fixture(autouse=True)
def _vcr_marker(request):
marker = request.node.get_closest_marker("vcr")
if marker:
cassette = request.getfixturevalue("vcr_cassette")
vcr = request.getfixturevalue("vcr")
request.node.__vcr_fixtures = dict(vcr_cassette=cassette, vcr=vcr)
yield
Run Code Online (Sandbox Code Playgroud)
但我需要的不仅仅是磁带才能找到我的解决方案。
pytest_exception_interact钩子arthurHamon2了测试套件并集成了匹配器差分输出。raise ... from ...抛出异常的形式vcrpy这些补丁在v2.1.0中发布
pip install vcrpy==2.1.0
Run Code Online (Sandbox Code Playgroud)
pytest_exception_interact钩子在测试目录的根目录中创建一个conftest.py来创建覆盖钩子的本地插件。pytest_exception_interact
@pytest.hookimpl(hookwrapper=True)
def pytest_exception_interact(node, call, report):
"""Intercept specific exceptions from tests."""
if report.when == "call" and isinstance(call.excinfo.value, CannotOverwriteExistingCassetteException):
__handle_cassette_exception(node, call, report)
yield
Run Code Online (Sandbox Code Playgroud)
从异常中提取 theCassette和 the 。Request
# Define new exception to throw
class RequestNotFoundCassetteException(Exception):
...
def __handle_cassette_exception(node, call, report):
# Safely check for attributes attached to exception
vcr_request = None
cassette = None
if hasattr(call.excinfo.value, "cassette"):
cassette = call.excinfo.value.cassette
if hasattr(call.excinfo.value, "failed_request"):
vcr_request = call.excinfo.value.failed_request
# If we have the extra context available...
if cassette and vcr_request:
match_properties = [f.__name__ for f in cassette._match_on]
this_req, req_str = __format_near_match(cassette.requests, vcr_request, match_properties)
try:
raise RequestNotFoundCassetteException(f"{this_req}\n\n{req_str}\n") from call.excinfo.value
except RequestNotFoundCassetteException as e:
call.excinfo._excinfo = (type(e), e)
report.longrepr = node.repr_failure(call.excinfo)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1446 次 |
| 最近记录: |