pytest-dev / pytest-flask

A set of pytest fixtures to test Flask applications

Home Page:http://pytest-flask.readthedocs.org/en/latest/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

pytest_flask.plugin.JSONResponse PicklingError

crazygit opened this issue · comments

The app code

from flask import Flask,jsonify
from flask_caching import Cache

CACHE_TYPE = 'redis'
CACHE_KEY_PREFIX = 'caches:'
CACHE_REDIS_URL = 'redis://localhost:8888/0'
DEBUG = True
CACHE_DEFAULT_TIMEOUT = 5 * 60  # seconds

app = Flask(__name__)
app.config.from_object(__name__)

cache = Cache(app)


@app.route('/')
@cache.cached(query_string=True)
def index():
    return jsonify({"message": "Hello world"})


if __name__ == '__main__':
    app.run()

when test with pytest_flask

from flask import url_for

def test_get_index(client):
    assert client.get(url_for('index')).status_code == 200

the error output

 _pickle.PicklingError: Can't pickle <class 'pytest_flask.plugin.<class 'pytest_flask.plugin.JSONResponse'>'>: attribute lookup <class 'pytest_flask.plugin.JSONResponse'> on pytest_flask.plugin failed

When debug the error, I found the error was caused by pytest_flask dynamic changed the Flask Response in pytest_flask/plugin.py with type .

def _make_test_response_class(response_class):
    """Extends the response class with special attribute to test JSON
    responses. Don't override user-defined `json` attribute if any.

    :param response_class: An original response class.
    """
    if 'json' in response_class.__dict__:
        return response_class

    return type(str(JSONResponse), (response_class, JSONResponse), {})

But I don't kwon how to fix this issure. Any suggestion on pickle dynamic class ?

I ran into the same issue. I was able to get farther by setting up a response class that has its own implementation of the json property. That keeps pytest_flask from trying to replace the class at runtime.

from flask import json, Response


class TestingResponse(Response):
    @property
    def json(self):
        return json.loads(self.data)


@pytest.fixture(scope="session")
def app(session_schema):
    app = create_app()
    app.response_class = TestingResponse
    return app

Compared to pytest_flask's JSONResponse, this doesn't cache the json and it doesn't override __eq__ and __ne__. (I haven't really dug into the implications, but this seems to fix my problem.)

@crazygit, @ianmclaury Thanks for documenting the issue and providing a solution so others facing the same problem can find it.

The provided example works fine on my environment

macOS Catalina
python 3.7.8
pytest==6.1.0
pytest-cache==1.0
Flask==1.1.2
Flask-Caching==1.9.0
redis==3.5.3

Could you please provide more details about your environments so I can try to reproduce the issue?

I have created a minimal application containing the provided code which is supposed to produce the issue, however, tests are executing successfully.

===================================== test session starts ======================================
platform darwin -- Python 3.8.5, pytest-6.1.1, py-1.9.0, pluggy-0.13.1
plugins: flask-1.0.0
collected 1 item
test_get.py . [100%]
================================= 1 passed in 0.06s ===========================================

I'll be closing this for the time being, feel free to reopen it to provide a minimal, reproducible example.