Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce fixture for test client class redefinition #4706

Merged
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES/4706.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add a fixture ``aiohttp_client_cls`` that allows usage of ``aiohttp.test_utils.TestClient`` custom implementations in tests.
1 change: 1 addition & 0 deletions CONTRIBUTORS.txt
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ Nikolay Kim
Nikolay Novik
Oisin Aylward
Olaf Conradi
Oleg Höfling
Pahaz Blinov
Panagiotis Kolokotronis
Pankaj Pandey
Expand Down
33 changes: 30 additions & 3 deletions aiohttp/pytest_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,34 @@ async def finalize(): # type: ignore


@pytest.fixture
def aiohttp_client(loop): # type: ignore
def aiohttp_client_cls(): # type: ignore
"""
Client class to use in ``aiohttp_client`` factory.

Use it for passing custom ``TestClient`` implementations.

Example::

class MyClient(TestClient):
async def login(self, *, user, pw):
payload = {"username": user, "password": pw}
return await self.post("/login", json=payload)

@pytest.fixture
def aiohttp_client_cls():
return MyClient

def test_login(aiohttp_client):
app = web.Application()
client = await aiohttp_client(app)
await client.login(user="admin", pw="s3cr3t")

"""
return TestClient


@pytest.fixture
def aiohttp_client(loop, aiohttp_client_cls): # type: ignore
"""Factory to create a TestClient instance.

aiohttp_client(app, **kwargs)
Expand All @@ -303,9 +330,9 @@ async def go(__param, *, server_kwargs=None, **kwargs): # type: ignore
if isinstance(__param, Application):
server_kwargs = server_kwargs or {}
server = TestServer(__param, **server_kwargs)
client = TestClient(server, **kwargs)
client = aiohttp_client_cls(server, **kwargs)
elif isinstance(__param, BaseTestServer):
client = TestClient(__param, **kwargs)
client = aiohttp_client_cls(__param, **kwargs)
else:
raise ValueError("Unknown argument type: %r" % type(__param))

Expand Down
47 changes: 47 additions & 0 deletions docs/testing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,53 @@ Pytest tooling has the following fixtures:

The fixture was renamed from ``unused_port`` to ``aiohttp_unused_port``.

.. data:: aiohttp_client_cls

A fixture for passing custom :class:`~aiohttp.test_utils.TestClient` implementations::

class MyClient(TestClient):
async def login(self, *, user, pw):
payload = {"username": user, "password": pw}
return await self.post("/login", json=payload)

@pytest.fixture
def aiohttp_client_cls():
return MyClient

def test_login(aiohttp_client):
app = web.Application()
client = await aiohttp_client(app)
await client.login(user="admin", pw="s3cr3t")

If you want to switch between different clients in tests, you can use
the usual ``pytest`` machinery. Example with using test markers::

class RESTfulClient(TestClient):
...

class GraphQLClient(TestClient):
...

@pytest.fixture
def aiohttp_client_cls(request):
if request.node.get_closest_marker('rest') is not None:
return RESTfulClient
if request.node.get_closest_marker('graphql') is not None:
return GraphQLClient
return TestClient


@pytest.mark.rest
async def test_rest(aiohttp_client) -> None:
client: RESTfulClient = await aiohttp_client(Application())
...


@pytest.mark.graphql
async def test_graphql(aiohttp_client) -> None:
client: GraphQLClient = await aiohttp_client(Application())
...


.. _aiohttp-testing-unittest-example:

Expand Down
73 changes: 73 additions & 0 deletions tests/test_pytest_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,3 +255,76 @@ def test_warnings_propagated(recwarn):
message = recwarn[0].message
assert isinstance(message, UserWarning)
assert message.args == ('test warning is propagated',)


def test_aiohttp_client_cls_fixture_custom_client_used(testdir) -> None:
testdir.makepyfile("""
import pytest
from aiohttp.web import Application
from aiohttp.test_utils import TestClient


class CustomClient(TestClient):
pass


@pytest.fixture
def aiohttp_client_cls():
return CustomClient


async def test_hello(aiohttp_client) -> None:
client = await aiohttp_client(Application())
assert isinstance(client, CustomClient)

""")
testdir.makeconftest(CONFTEST)
result = testdir.runpytest()
result.assert_outcomes(passed=1)


def test_aiohttp_client_cls_fixture_factory(testdir) -> None:
testdir.makeconftest(CONFTEST + """

def pytest_configure(config):
config.addinivalue_line("markers", "rest: RESTful API tests")
config.addinivalue_line("markers", "graphql: GraphQL API tests")

""")
testdir.makepyfile("""
import pytest
from aiohttp.web import Application
from aiohttp.test_utils import TestClient


class RESTfulClient(TestClient):
pass


class GraphQLClient(TestClient):
pass


@pytest.fixture
def aiohttp_client_cls(request):
if request.node.get_closest_marker('rest') is not None:
return RESTfulClient
elif request.node.get_closest_marker('graphql') is not None:
return GraphQLClient
return TestClient


@pytest.mark.rest
async def test_rest(aiohttp_client) -> None:
client = await aiohttp_client(Application())
assert isinstance(client, RESTfulClient)


@pytest.mark.graphql
async def test_graphql(aiohttp_client) -> None:
client = await aiohttp_client(Application())
assert isinstance(client, GraphQLClient)

""")
result = testdir.runpytest()
result.assert_outcomes(passed=2)