pytest-dev / pytest-dev/pytest-asyncio

Is there a way to fail a test if an explicitly created background task fails after yielding from the fixture that created it?

Open
#196 8 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
1.7k
Forks
207
Avg merge
5h 35m
Merged PRs (30d)
9

Description

I'll start with an example to provide some context:

import asyncio
import contextlib
import itertools

import pytest


class Consumer:
    def __init__(self, queue: asyncio.Queue):
        self._queue = queue

    async def run(self):
        for i in itertools.count(0):
            item = await self._queue.get()
            print(item)

            if i == 3:
                raise ValueError('oups')

            self._queue.task_done()


@pytest.fixture
async def queue() -> asyncio.Queue:
    return asyncio.Queue()


@pytest.fixture
async def consumer_service(queue):
    consumer = Consumer(queue)
    background_task = asyncio.create_task(consumer.run())

    yield

    background_task.cancel()
    with contextlib.suppress(asyncio.CancelledError):
        await run_task


@pytest.mark.asyncio
@pytest.mark.usefixtures('consumer_service')
async def test_consumer_consumes(queue: asyncio.Queue):
    queue.put_nowait('I')
    queue.put_nowait('Will')
    queue.put_nowait('Not')
    queue.put_nowait('Hang')

    await queue.join()

Here, the consumer service fixture will create a background task that runs the consumer's loop. When processing the last item in the queue, a value error is raised and the background task finishes with an exception before marking the last item as done. In this state, the test will hang and there will be no indication that something went astray.

Ideally, there would be some way to react to the background task failing by canceling and failing the test but I have not seen a way to do it. The closest I got to a solution was to attach a done callback to the background task and check for an exception there but I haven't come across an API that would allow me to recover from this or a way to signal the error (without it being captured by pytest).

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the asyncio fixture and background-task example in this issue, focusing on how pytest-asyncio handles tasks created before a fixture yields. Investigate whether a task exception can cancel and fail the test instead of leaving queue.join() hanging. Done means the shown failure is reported by pytest without requiring an ad hoc callback.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
testing-qa
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.