From 2efdf95f43877fd05e6e15300e4f95b988761dc8 Mon Sep 17 00:00:00 2001 From: Anas Date: Wed, 18 Nov 2020 22:45:51 +0200 Subject: [PATCH] [3.8] AioHTTPTestCase more async friendly (#4732) Co-authored-by: Andrew Svetlov . (cherry picked from commit 53578589c34a75951b4f62e1ac019c1bac6f228f) Co-authored-by: Anas --- CHANGES/4700.feature | 6 ++++++ CONTRIBUTORS.txt | 1 + aiohttp/test_utils.py | 34 +++++++++++++++------------------- requirements/base.txt | 1 + setup.cfg | 4 ++++ setup.py | 1 + tests/test_test_utils.py | 17 ++++++++++++++++- 7 files changed, 44 insertions(+), 20 deletions(-) create mode 100644 CHANGES/4700.feature 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 9fcfefa02a8..3da9237d84b 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 7a9ca7ddf3e..3923f2b8c76 100644 --- a/aiohttp/test_utils.py +++ b/aiohttp/test_utils.py @@ -8,7 +8,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 @@ -18,17 +17,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 ( @@ -48,6 +43,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" @@ -400,7 +399,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. @@ -434,26 +433,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 0d129267051..163d8703671 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -3,6 +3,7 @@ aiodns==2.0.0; sys_platform=="linux" or sys_platform=="darwin" and python_version>="3.7" async-generator==1.10 async-timeout==3.0.1 +asynctest==0.13.0; python_version<"3.8" attrs==20.3.0 brotlipy==0.7.0 cchardet==2.1.7 diff --git a/setup.cfg b/setup.cfg index df8fbc3152f..ed96ca05d68 100644 --- a/setup.cfg +++ b/setup.cfg @@ -101,3 +101,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 428df5d4e95..8755a13f2c3 100644 --- a/setup.py +++ b/setup.py @@ -69,6 +69,7 @@ def build_extension(self, ext): "chardet>=2.0,<4.0", "multidict>=4.5,<7.0", "async_timeout>=3.0,<4.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 cbaed33bccd..c268e71073f 100644 --- a/tests/test_test_utils.py +++ b/tests/test_test_utils.py @@ -109,7 +109,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 @@ -118,6 +118,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: