Skip to content

Commit

Permalink
Allow to use the mark at different levels
Browse files Browse the repository at this point in the history
  • Loading branch information
Colin-b committed Sep 23, 2024
1 parent 549622b commit 419e49e
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 15 deletions.
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.31.2] - 2024-09-23
### Fixed
- `httpx_mock` marker can now be defined at different levels for a single test.

## [0.31.1] - 2024-09-22
### Fixed
- It is now possible to match on content provided as async iterable by the client.
Expand Down Expand Up @@ -341,7 +345,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- First release, should be considered as unstable for now as design might change.

[Unreleased]: https://github.com/Colin-b/pytest_httpx/compare/v0.31.1...HEAD
[Unreleased]: https://github.com/Colin-b/pytest_httpx/compare/v0.31.2...HEAD
[0.31.2]: https://github.com/Colin-b/pytest_httpx/compare/v0.31.1...v0.31.2
[0.31.1]: https://github.com/Colin-b/pytest_httpx/compare/v0.31.0...v0.31.1
[0.31.0]: https://github.com/Colin-b/pytest_httpx/compare/v0.30.0...v0.31.0
[0.30.0]: https://github.com/Colin-b/pytest_httpx/compare/v0.29.0...v0.30.0
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<a href="https://github.com/Colin-b/pytest_httpx/actions"><img alt="Build status" src="https://github.com/Colin-b/pytest_httpx/workflows/Release/badge.svg"></a>
<a href="https://github.com/Colin-b/pytest_httpx/actions"><img alt="Coverage" src="https://img.shields.io/badge/coverage-100%25-brightgreen"></a>
<a href="https://github.com/psf/black"><img alt="Code style: black" src="https://img.shields.io/badge/code%20style-black-000000.svg"></a>
<a href="https://github.com/Colin-b/pytest_httpx/actions"><img alt="Number of tests" src="https://img.shields.io/badge/tests-215 passed-blue"></a>
<a href="https://github.com/Colin-b/pytest_httpx/actions"><img alt="Number of tests" src="https://img.shields.io/badge/tests-216 passed-blue"></a>
<a href="https://pypi.org/project/pytest-httpx/"><img alt="Number of downloads" src="https://img.shields.io/pypi/dm/pytest_httpx"></a>
</p>

Expand Down
8 changes: 6 additions & 2 deletions pytest_httpx/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from collections.abc import Generator
from operator import methodcaller

import httpx
import pytest
Expand All @@ -20,8 +21,11 @@ def httpx_mock(
monkeypatch: MonkeyPatch,
request: FixtureRequest,
) -> Generator[HTTPXMock, None, None]:
marker = request.node.get_closest_marker("httpx_mock")
options = HTTPXMockOptions.from_marker(marker) if marker else HTTPXMockOptions()
options = {}
for marker in request.node.iter_markers("httpx_mock"):
options = marker.kwargs | options
__tracebackhide__ = methodcaller("errisinstance", TypeError)
options = HTTPXMockOptions(**options)

mock = HTTPXMock()

Expand Down
12 changes: 2 additions & 10 deletions pytest_httpx/_httpx_mock.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import copy
import inspect
from operator import methodcaller
from typing import Union, Optional, Callable, Any, NoReturn, AsyncIterable
from collections.abc import Awaitable, Iterable
from typing import Union, Optional, Callable, Any, NoReturn
from collections.abc import Awaitable

import httpx
from pytest import Mark

from pytest_httpx import _httpx_internals
from pytest_httpx._pretty_print import RequestDescription
Expand All @@ -32,12 +30,6 @@ def __init__(
]
self.non_mocked_hosts = [*non_mocked_hosts, *missing_www]

@classmethod
def from_marker(cls, marker: Mark) -> "HTTPXMockOptions":
"""Initialise from a marker so that the marker kwargs raise an error if incorrect."""
__tracebackhide__ = methodcaller("errisinstance", TypeError)
return cls(**marker.kwargs)


class HTTPXMock:
def __init__(self) -> None:
Expand Down
2 changes: 1 addition & 1 deletion pytest_httpx/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
# Major should be incremented in case there is a breaking change. (eg: 2.5.8 -> 3.0.0)
# Minor should be incremented in case there is an enhancement. (eg: 2.5.8 -> 2.6.0)
# Patch should be incremented in case there is a bug fix. (eg: 2.5.8 -> 2.5.9)
__version__ = "0.31.1"
__version__ = "0.31.2"
55 changes: 55 additions & 0 deletions tests/test_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,61 @@ async def test_httpx_mock_non_mocked_hosts_async(httpx_mock):
result.assert_outcomes(passed=1)


def test_httpx_mock_options_on_multi_levels_are_aggregated(testdir: Testdir) -> None:
"""
Test case ensures that every level provides one parameter that should be used in the end
global (actually registered AFTER module): assert_all_responses_were_requested (tested by putting unused response)
module: assert_all_requests_were_expected (tested by not mocking one URL)
test: non_mocked_hosts (tested by calling 3 URls, 2 mocked, the other one not)
"""
testdir.makeconftest(
"""
import pytest
def pytest_collection_modifyitems(session, config, items):
for item in items:
item.add_marker(pytest.mark.httpx_mock(assert_all_responses_were_requested=False))
"""
)
testdir.makepyfile(
"""
import httpx
import pytest
pytestmark = pytest.mark.httpx_mock(assert_all_requests_were_expected=False, non_mocked_hosts=["https://foo.tld"])
@pytest.mark.asyncio
@pytest.mark.httpx_mock(non_mocked_hosts=["localhost"])
async def test_httpx_mock_non_mocked_hosts_async(httpx_mock):
httpx_mock.add_response(url="https://foo.tld", headers={"x-pytest-httpx": "this was mocked"})
# This response will never be used, testing that assert_all_responses_were_requested is handled
httpx_mock.add_response(url="https://never_called.url")
async with httpx.AsyncClient() as client:
# Assert that previously set non_mocked_hosts was overridden
response = await client.get("https://foo.tld")
assert response.headers["x-pytest-httpx"] == "this was mocked"
# Assert that latest non_mocked_hosts is handled
with pytest.raises(httpx.ConnectError):
await client.get("https://localhost:5005")
# Assert that assert_all_requests_were_expected is the one at module level
with pytest.raises(httpx.TimeoutException):
await client.get("https://unexpected.url")
# Assert that 2 requests out of 3 were mocked
assert len(httpx_mock.get_requests()) == 2
"""
)
result = testdir.runpytest()
result.assert_outcomes(passed=1)


def test_invalid_marker(testdir: Testdir) -> None:
"""
Unknown marker keyword arguments should raise a TypeError.
Expand Down

0 comments on commit 419e49e

Please sign in to comment.