如何从视图中删除“@oidc.login_required”以进行单元测试?

Posted

技术标签:

【中文标题】如何从视图中删除“@oidc.login_required”以进行单元测试?【英文标题】:How can I remove "@oidc.login_required" for unit testing from a view? 【发布时间】:2019-12-06 15:31:28 【问题描述】:

我使用flask-oidc 进行用户登录,使用pytest 进行测试。对于单元测试,我想“删除”@oidc.require_login。我该怎么做?

我尝试了什么

flask-o 的工作方式大致如下:

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)

然后我希望单元测试可以模拟掉@oidc.require_login

# 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】:

首先通过 pip 安装 blinker。我不知道为什么,但它是必需的。

那么这对我有用:

# 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': 'foo@bar.com'
    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'

灵感来自https://github.com/fedora-infra/elections

【讨论】:

以上是关于如何从视图中删除“@oidc.login_required”以进行单元测试?的主要内容,如果未能解决你的问题,请参考以下文章

如何在运行时从超级视图中删除视图?

如何添加多个视图和删除视图并在删除后从视图中获取所有数据?

如何从集合视图中删除项目?

如何从视图中删除列

如何从表视图中添加/删除子视图

如何从 XIB 中删除表视图?