Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing Flask test client session in pytest test when using an app factory

I'm trying to unittest an application using pytest and an app factory, but I can't seem to get access to the client session object in my tests. I'm sure there's some context I'm not pushing somewhere. I push the app context in my 'app' fixture. Should I push the request context somewhere?

The following is an MWE.

mwe.py:

from flask import Flask, session


def create_app():
    app = Flask(__name__)
    app.secret_key = 'top secret'

    @app.route('/set')
    def session_set():
        session['key'] = 'value'
        return 'Set'

    @app.route('/check')
    def session_check():
        return str('key' in session)

    @app.route('/clear')
    def session_clear():
        session.pop('key', None)
        return 'Cleared'

    return app


if __name__ == "__main__":
    mwe = create_app()
    mwe.run()

conftest.py:

import pytest
from mwe import create_app


@pytest.fixture(scope='session')
def app(request):
    app = create_app()

    ctx = app.app_context()
    ctx.push()

    def teardown():
        ctx.pop()

    request.addfinalizer(teardown)
    return app


@pytest.fixture(scope='function')
def client(app):
    return app.test_client()

test_session.py:

import pytest
from flask import session


def test_value_set_for_client_request(client):  # PASS
    client.get('/set')
    r = client.get('/check')
    assert 'True' in r.data


def test_value_set_in_session(client):  # FAIL
    client.get('/set')
    assert 'key' in session


def test_value_set_in_session_transaction(client):  # FAIL
    with client.session_transaction() as sess:
        client.get('/set')
        assert 'key' in sess

Note that running this directly works fine, I can jump around /set, /check, /clear and it behaves as expected. Similarly, the test that uses only the test client to GET the pages works as expected. Accessing the session directly however doesn't seem to.

like image 539
R. Jin Avatar asked Oct 30 '22 23:10

R. Jin


2 Answers

The problem is with the way you use your test client.

First of all you don't have to create your client fixture. If you use pytest-flask, it offers a client fixture to use. If you still want to use your own client though (maybe because you don't want pytest-flask), your client fixture should act as a context processor to wrap your requests around.

So you need something like the following:

def test_value_set_in_session(client):
    with client:
        client.get('/set')
        assert 'key' in session

information of the day: pytest-flask has a similar client fixture to yours. The difference is that pytest-flask uses a context manager to give you a client and that saves you 1 line per test

@pytest.yield_fixture
def client(app):
    """A Flask test client. An instance of :class:`flask.testing.TestClient`
    by default.
    """
    with app.test_client() as client:
        yield client

your test with pytest-flask client

def test_value_set_in_session(client):
    client.get('/set')
    assert 'key' in session
like image 153
John Paraskevopoulos Avatar answered Nov 11 '22 08:11

John Paraskevopoulos


Take a look at the following from conftest.py in cookiecutter-flask. It may give you some ideas.

@pytest.yield_fixture(scope='function')
def app():
    """An application for the tests."""
    _app = create_app(TestConfig)
    ctx = _app.test_request_context()
    ctx.push()

    yield _app

    ctx.pop()


@pytest.fixture(scope='function')
def testapp(app):
    """A Webtest app."""
    return TestApp(app)
like image 36
Ali Cirik Avatar answered Nov 11 '22 07:11

Ali Cirik