Sazzad
Sazzad

Reputation: 853

write unit test for async aiohttp and aio files

I am new with asyncio. I am using aiohttp and aiofiles for downloading images. how to write the unit tests for both of these.

class ImageDownloader:
    def __init__(self, folder_path: str):
        self.folder_path = folder_path

    async def async_download_image(self, image_name: str, image_url: str):
        logging.info("%s downloading is started", image_name)
        async with aiohttp.ClientSession() as session:
            async with session.get(image_url) as resp:
                if resp.status == 200:
                    logging.info(" %s downloading is finished", image_name)
                    image_saving_path = os.path.join(self.folder_path, image_name)
                    logging.info(" %s saving to directory is started", image_name)
                    file = await aiofiles.open(image_saving_path, mode='wb')
                    await file.write(await resp.read())
                    await file.close()
                    logging.info(" %s saving to directory is finished", image_name)
                else:
                    logging.exception(IMAGE_DOWNLOADER_EXCEPTION + image_name)
                    raise ImageDownloaderError(IMAGE_DOWNLOADER_EXCEPTION + image_name)

Upvotes: 0

Views: 779

Answers (1)

CodeNStuff
CodeNStuff

Reputation: 314

Since Python 3.8 there is unittest.IsolatedAsyncioTestCase which let's you test your write unittest for any asyncio code conveniently:

class MyFixture(unittest.IsolatedAsyncioTestCase):
    async def test_1(self):
        result = await production_code()

        self.assertEqual(result, 42)

Regarding aiohttp it is officially recommended (see warning in the docs "Faking request object") to run a local server to test your client. To be honest, I have no clue why, as it disagrees with the standard rule to mock expensive dependencies. Anyway, to do so you have to redesign your function so it's accepting the session object as a parameter. This way, you can redirect the requests with the help of a mocked resolver to your local test server.

async def production_code(client_session):
    aync with client_session.get(...) as response:
        ...

...
    async def test_2(self):
        with create_mocked_session() as mock:
            await production_code(mock)
        ...

It may be easier to bypass the whole aiohttp lib completely by mocking the session object itself and yielding prepare handcrafted test responses.

I have no idea about aiofiles but the same pattern holds for file input/output as well. Pass in a mocked file_like. which preferably holds everything in memory.

Upvotes: 1

Related Questions