diff --git a/CHANGES/4700.feature b/CHANGES/4700.feature new file mode 100644 index 00000000000..dfcd88ff960 --- /dev/null +++ b/CHANGES/4700.feature @@ -0,0 +1,6 @@ +AioHTTPTestCase is more async friendly now. + +For people who use unittest and are used to use unittest.TestCase +it will be easier to write new test cases like the sync version of the TestCase class, +without using the decorator `@unittest_run_loop`, just `async def test_*`. +The only difference is that for the people using python3.7 and below a new dependency is needed, it is `asynctestcase`. diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 8341346ea9d..b90914a4f2b 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -30,6 +30,7 @@ Alexey Stepanov Amin Etesamian Amit Tulshyan Amy Boyle +Anas El Amraoui Anders Melchiorsen Andrei Ursulenko Andrej Antonov diff --git a/aiohttp/test_utils.py b/aiohttp/test_utils.py index a4d2857d27a..57e2c40027f 100644 --- a/aiohttp/test_utils.py +++ b/aiohttp/test_utils.py @@ -9,7 +9,6 @@ import os import socket import sys -import unittest from abc import ABC, abstractmethod from types import TracebackType from typing import TYPE_CHECKING, Any, Callable, Iterator, List, Optional, Type, Union @@ -19,17 +18,13 @@ from yarl import URL import aiohttp -from aiohttp.client import ( - ClientResponse, - _RequestContextManager, - _WSRequestContextManager, -) +from aiohttp.client import _RequestContextManager, _WSRequestContextManager from . import ClientSession, hdrs from .abc import AbstractCookieJar from .client_reqrep import ClientResponse from .client_ws import ClientWebSocketResponse -from .helpers import sentinel +from .helpers import PY_38, sentinel from .http import HttpVersion, RawRequestMessage from .signals import Signal from .web import ( @@ -49,6 +44,10 @@ else: SSLContext = None +if PY_38: + from unittest import IsolatedAsyncioTestCase as TestCase +else: + from asynctest import TestCase # type: ignore REUSE_ADDRESS = os.name == "posix" and sys.platform != "cygwin" @@ -386,7 +385,7 @@ async def __aexit__( await self.close() -class AioHTTPTestCase(unittest.TestCase): +class AioHTTPTestCase(TestCase): """A base class to allow for unittest web applications using aiohttp. @@ -420,26 +419,23 @@ def get_app(self) -> Application: raise RuntimeError("Did you forget to define get_application()?") def setUp(self) -> None: - self.loop = setup_test_loop() - - self.app = self.loop.run_until_complete(self.get_application()) - self.server = self.loop.run_until_complete(self.get_server(self.app)) - self.client = self.loop.run_until_complete(self.get_client(self.server)) - - self.loop.run_until_complete(self.client.start_server()) + if PY_38: + self.loop = asyncio.get_event_loop() self.loop.run_until_complete(self.setUpAsync()) async def setUpAsync(self) -> None: - pass + self.app = await self.get_application() + self.server = await self.get_server(self.app) + self.client = await self.get_client(self.server) + + await self.client.start_server() def tearDown(self) -> None: self.loop.run_until_complete(self.tearDownAsync()) - self.loop.run_until_complete(self.client.close()) - teardown_test_loop(self.loop) async def tearDownAsync(self) -> None: - pass + await self.client.close() async def get_server(self, app: Application) -> TestServer: """Return a TestServer instance.""" diff --git a/requirements/base.txt b/requirements/base.txt index 393ac98e593..6c64cd9cccf 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -2,6 +2,7 @@ # required c-ares will not build on windows and has build problems on Macos Python<3.7 aiodns==2.0.0; sys_platform=="linux" or sys_platform=="darwin" and python_version>="3.7" async-timeout==4.0.0a3 +asynctest==0.13.0; python_version<"3.8" attrs==20.3.0 Brotli==1.0.9 cchardet==2.1.7 diff --git a/setup.cfg b/setup.cfg index 4f8ea4b7360..7cb78266eb8 100644 --- a/setup.cfg +++ b/setup.cfg @@ -97,3 +97,7 @@ ignore_missing_imports = true [mypy-idna_ssl] ignore_missing_imports = true + + +[mypy-asynctest] +ignore_missing_imports = true diff --git a/setup.py b/setup.py index 9e26b003e92..da5ae3ad485 100644 --- a/setup.py +++ b/setup.py @@ -55,6 +55,7 @@ "chardet>=2.0,<4.0", "multidict>=4.5,<7.0", "async_timeout>=4.0a2,<5.0", + 'asynctest==0.13.0; python_version<"3.8"', "yarl>=1.0,<2.0", 'idna-ssl>=1.0; python_version<"3.7"', "typing_extensions>=3.6.5", diff --git a/tests/test_test_utils.py b/tests/test_test_utils.py index 7a94451dbc4..afc60bd2830 100644 --- a/tests/test_test_utils.py +++ b/tests/test_test_utils.py @@ -95,7 +95,7 @@ async def test_example_with_loop(self) -> None: text = await request.text() assert _hello_world_str == text - def test_example(self) -> None: + def test_inner_example(self) -> None: async def test_get_route() -> None: resp = await self.client.request("GET", "/") assert resp.status == 200 @@ -104,6 +104,21 @@ async def test_get_route() -> None: self.loop.run_until_complete(test_get_route()) + async def test_example_without_explicit_loop(self) -> None: + request = await self.client.request("GET", "/") + assert request.status == 200 + text = await request.text() + assert _hello_world_str == text + + async def test_inner_example_without_explicit_loop(self) -> None: + async def test_get_route() -> None: + resp = await self.client.request("GET", "/") + assert resp.status == 200 + text = await resp.text() + assert _hello_world_str == text + + await test_get_route() + def test_get_route(loop, test_client) -> None: async def test_get_route() -> None: