How can I remove "@oidc.login_required" for unit testing from a view?

1.3k Views Asked by At

I use for user login and for testing. For unit testing, I would like to "remove" @oidc.require_login. How can I do that?

What I tried

The way flask-o works is roughly:

from flask import Flask, url_for, redirect
from flask_oidc import OpenIDConnect

app = Flask(__name__)
app.config['OIDC_CLIENT_SECRETS'] = 'client_secrets.json'
# Contents:
# Create client_id and client_secret at https://console.developers.google.com/apis/credentials
# {
#     "web": {
#         "client_id": "123456789012-abc123hi09123.apps.googleusercontent.com",
#         "client_secret": "ab123456789ABCDEFGHIJKLM",
#         "redirect_uris": ["http://localhost:5000"],
#         "auth_uri": "https://accounts.google.com/o/oauth2/auth",
#         "token_uri": "https://accounts.google.com/o/oauth2/token",
#         "userinfo_uri": "https://www.googleapis.com/oauth2/v3/userinfo"
#     }
# }
app.config['SECRET_KEY'] = 'uq4aKjUvWXTPTIyfCz7mTtcG'
app.config['OIDC_ID_TOKEN_COOKIE_SECURE'] = False
app.config['OIDC_SCOPES'] = ["openid", "profile", "email"]
app.config['OIDC_CALLBACK_ROUTE'] = '/authorization-code/callback'
oidc = OpenIDConnect(app)


@app.route('/')
@oidc.require_login
def index():
    return redirect(url_for('personalized'))


@app.route('/personalized')
@oidc.require_login
def personalized():
    info = oidc.user_getinfo(['email', 'openid_id'])
    return 'Hello, {} ({})'.format(info.get('email'), info.get('openid_id'))


@app.route('/hello')
@oidc.require_login
def constant():
    return 'Hello'


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

Then I hoped the unit test could mock the @oidc.require_login away:

# core modules
from unittest import mock

# 3rd party modules
import pytest

# internal modules
import exampleapp


@pytest.fixture
def client():
    app = exampleapp.app
    client = app.test_client()
    yield client


@mock.patch("flask_oidc.OpenIDConnect")
def test_private(mock_require_login, client):
    rv = client.get('/hello')
    assert rv.data == b'Hello'
1

There are 1 best solutions below

0
On BEST ANSWER

First install blinker via pip. I'm not sure why, but it is required.

Then this works for me:

# core modules
from unittest import mock

# 3rd party modules
import pytest
from flask import appcontext_pushed, g

# internal modules
import exampleapp


@pytest.fixture
def client():
    app = exampleapp.app
    app.testing = True
    app.before_request_funcs[None] = []

    def handler(sender, **kwargs):
        g.oidc_id_token = {'sub': 'some-user-id', 'email': '[email protected]'}
    client = app.test_client()
    with appcontext_pushed.connected_to(handler, app):
        yield client


def test_private(client):
    with mock.patch.object(
        exampleapp.oidc, "validate_token", return_value=True
    ):
        rv = client.get('/hello')
        assert rv.data == b'Hello'

Inspired by https://github.com/fedora-infra/elections