chishaku
chishaku

Reputation: 4643

How can I mock functions that are called by async code?

This test works.

def test_mock_ingest():
    with mock.patch('app.ingest.ingest') as mock_ingest:
        app.ingest.ingest('test-data', {'test-key': 'test-value'})
        assert mock_ingest.call_count == 1

This test fails because mock_ingest.call_count = 0

def test_mock_ingest():
    with mock.patch('app.ingest.ingest') as mock_ingest:
        call_function_that_runs_async_code()
        assert mock_ingest.call_count == 1

call_function_that_runs_async_code calls the app.ingest.ingest function.

I know because I can see that the test data is ingested.

But for some reason, mock_ingest.call_count is still 0.

I think it has to do with the fact that the code that runs app.ingest.ingest is async.

Edit:

I'm using python 3.8.

I also tried this without success:

```python
def test_mock_ingest():
    with mock.patch('app.ingest.ingest', new_callable=mock.AsyncMock) as mock_ingest:
        call_function_that_runs_async_code()
        assert mock_ingest.call_count == 1

Upvotes: 3

Views: 804

Answers (1)

chishaku
chishaku

Reputation: 4643

The solution had nothing to do with async code after all.

call_function_that_runs_async_code wasn't calling app.ingest.ingest.

It was calling ingest after ingest was imported like this: from app.ingest import ingest.

That import is correct but due to namespace issues with mocking, importing the function in different ways across application code and test code does not work.

TIL that you patch functions where you import them, not where they are defined. https://docs.python.org/3/library/unittest.mock.html#where-to-patch

The correct solution code in my example should look like this:

def test_mock_ingest():
    with mock.patch('async_codebase.ingest') as mock_ingest:
        call_function_that_runs_async_code()
        assert mock_ingest.call_count == 1

where async_codebase includes the import:

from app.ingest import ingest

Upvotes: 2

Related Questions