Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to use pytest with cache #49

Open
devanchohan opened this issue Dec 20, 2021 · 15 comments
Open

Unable to use pytest with cache #49

devanchohan opened this issue Dec 20, 2021 · 15 comments
Labels
documentation Improvements or additions to documentation question Further information is requested

Comments

@devanchohan
Copy link

I have a few integration tests than run as part of jenkins job, but I have been unable to get pytests to run and connect to the redis cache.
I have tried this but had the same error https://fastapi.tiangolo.com/advanced/testing-events/

Has anyone found a way to test their routes that have a cache?

@devanchohan
Copy link
Author

Anyone faced this issue?
I tried the methods outlined in here: tiangolo/fastapi#2003 but I still hit the same issue and tests fail

@sac-cas
Copy link

sac-cas commented Feb 15, 2022

What problems do you encounter?
I finally handled the init error but unfortunately my service function used by the cached endpoint has not been called once.
Is there a way to enable/disable the cache for testing or to simulate a cached/uncached request?

@sergeytol
Copy link

I faced the same problem. It fails with "You must call init first!"

@hh-h
Copy link

hh-h commented May 27, 2022

add to your conftest.py file

from unittest import mock

mock.patch("fastapi_cache.decorator.cache", lambda *args, **kwargs: lambda f: f).start()

@Mark1002
Copy link

Mark1002 commented Nov 2, 2022

you also can try this code below to mock cache.

# conftest.py
from unittest import mock

def mock_cache(*args, **kwargs):
    def wrapper(func):
        @wraps(func)
        async def inner(*args, **kwargs):
            return await func(*args, **kwargs)
        return inner
    return wrapper

mock.patch("fastapi_cache.decorator.cache", mock_cache).start()    

@pytest.fixture(scope="module")
async def client():
    from app.main import app # need to load app module after mock. otherwise, it would fail
    async with AsyncClient(app=app, base_url="http://test") as client:
        yield client

@tpthian
Copy link

tpthian commented Mar 8, 2023

Hi @Mark1002,

Sorry for being a noop here , but I'm hitting error when trying to use the fixture client. How is client fixture supposed to be used in a test function ?

I have the conftest.py setup as above, then my code below yield error.

import pytest

from app.main import init_app

@pytest.mark.anyio
async def test_api_get_news_details(asyncclient):
    response = await asyncclient.get("/news/34986915")

    assert response.status_code == 200

and here's the error

fixture 'mock_decorator' not found
>       available fixtures: anyio_backend, anyio_backend_name, anyio_backend_options, cache, capfd, capfdbinary, caplog, capsys, capsysbinary, client, doctest_namespace, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory

What should I do now?

@shinyco
Copy link

shinyco commented Mar 17, 2023

@tpthian Just put the following code above your imports where you are calling the @cache

mock.patch("fastapi_cache.decorator.cache", lambda *args, **kwargs: lambda f: f).start()

@Mark1002
Copy link

@tpthian sorry this is my mistake. remove parameter mock_decorator in async def client. i have edit my code snippet above.

@mjpieters mjpieters added question Further information is requested documentation Improvements or additions to documentation labels May 15, 2023
@mjpieters
Copy link
Collaborator

This is a potential area where documentation could provide an example.

@CharlesPerrotMinotHCHB
Copy link
Contributor

you also can try this code below to mock cache.

# conftest.py
from unittest import mock

def mock_cache(*args, **kwargs):
    def wrapper(func):
        @wraps(func)
        async def inner(*args, **kwargs):
            return await func(*args, **kwargs)
        return inner
    return wrapper

mock.patch("fastapi_cache.decorator.cache", mock_cache).start()    

@pytest.fixture(scope="module")
async def client():
    from app.main import app # need to load app module after mock. otherwise, it would fail
    async with AsyncClient(app=app, base_url="http://test") as client:
        yield client

On my end, none of the suggested solutions worked for me, all failing with 'function' object has no attribute 'start'

@dicolasi
Copy link

I am struggling with the same identical problem

@dicolasi
Copy link

dicolasi commented Oct 20, 2023

ok this works for me.

Create a file called conftest.py within your module and paste this:

from unittest import mock


def mock_cache():
    mock.patch("fastapi_cache.decorator.cache", lambda *args, **kwargs: lambda f: f).start()


def pytest_sessionstart(session):
    mock_cache()

@Abramov0Alexandr
Copy link

@tpthianПросто поместите следующий код над импортом, где вы вызываете @cache

mock.patch("fastapi_cache.decorator.cache", lambda *args, **kwargs: lambda f: f).start()

thx for the reply it really helped me and solved this problem, but none linter allows to set this code on top😥. On the other hand other solutions don't help me, so we have what we have

@jonathanhle
Copy link

you also can try this code below to mock cache.

# conftest.py
from unittest import mock

def mock_cache(*args, **kwargs):
    def wrapper(func):
        @wraps(func)
        async def inner(*args, **kwargs):
            return await func(*args, **kwargs)
        return inner
    return wrapper

mock.patch("fastapi_cache.decorator.cache", mock_cache).start()    

@pytest.fixture(scope="module")
async def client():
    from app.main import app # need to load app module after mock. otherwise, it would fail
    async with AsyncClient(app=app, base_url="http://test") as client:
        yield client

this worked for me. thank you @Mark1002. i was stuck on this for sooooo long - here's the top of my conftest.py if it helps anyone else in the future:

from functools import wraps
from unittest import mock

import pytest
from fastapi.testclient import TestClient

# We need to disable the @cache decorator during testing
def mock_cache(*args, **kwargs):
    def wrapper(func):
        @wraps(func)
        async def inner(*args, **kwargs):
            return await func(*args, **kwargs)

        return inner

    return wrapper


mock.patch("fastapi_cache.decorator.cache", mock_cache).start()

# FastAPI "app" instance needs to be imported after this mocking
from ..main import app


@pytest.fixture(scope="module")
def client():
    with TestClient(app) as test_client:
        yield test_client

@PavelShaura
Copy link

I faced the same problem. It fails with "You must call init first!"

The error occurs because FastAPICache has not been initialized before use.
Here are steps you can take to solve this problem:

Initialize FastAPICache in your main application file (usually main.py). You should do this after creating an instance of FastAPI.
For tests, you will need to create a dummy cache backend.

This solution worked for me:
1.In your main application file (e.g. app/main.py):

@app.on_event("startup").
async def startup():
FastAPICache.init(InMemoryBackend())

2.In the conftest.py file, added the following:

@pytest.fixture(autouse=True, scope="function")
def fastapi_cache():
FastAPICache.init(InMemoryBackend())

  1. Make sure your tests import and use this fixture:

@pytest.mark.asyncio
async def test_add_data(ac: AsyncClient, fastapi_cache):
# Your test code is here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation question Further information is requested
Projects
None yet
Development

No branches or pull requests