简体   繁体   English

Mocking auth 装饰器与 pytest 和 flask

[英]Mocking auth decorator with pytest and flask

I have a flask application that uses an auth decorator to validate JWT tokens to an external service (auth0).我有一个 flask 应用程序,它使用 auth 装饰器将 JWT 令牌验证到外部服务 (auth0)。 The application looks like this:该应用程序如下所示:

app/helpers/decorators.py应用程序/助手/装饰器.py

from functools import wraps

def check_auth(f):
    @wraps(f)
    def auth_check(*args, **kwargs):
        token = get_token_auth_header() # will get token from Bearer header
        jsonurl = urlopen("https://"+AUTH0_DOMAIN+"/.well-known/jwks.json")
        ...[ pass token to auth0 ]
    return auth_check

app/api/annotation_api.py应用程序/api/annotation_api.py

from flask.views import MethodView
from app.helpers.decorators import check_auth

class AnnotationMetricAPI(MethodView):
    @check_auth
    def get(self, id):
        return {status: 200}

annotation_metric_view = AnnotationMetricAPI.as_view('annotation_metric_view')

app/routes/routes.py应用程序/路线/路线.py

from flask import Blueprint
from flask_cors import CORS
from app.api.annotation_api import annotation_metric_view

routes_blueprint = Blueprint('routes', __name__)
CORS(routes_blueprint, max_age=30*86400)

routes_blueprint.add_url_rule(
    '/api/v1/annotation/metric/<id>',
    view_func=annotation_metric_view,
    methods=['GET']
)

app/_ _init_ _.py应用程序/_ _init_ _.py

from flask import Flask
from flask_cors import CORS
from app.routes import routes_blueprint
import logging

logging.basicConfig(level=logging.DEBUG)


def create_app():
    app = Flask(__name__, instance_relative_config=False)
    app.config.from_object('config.Config')
    CORS(app)
    with app.app_context():
        app.register_blueprint(routes_blueprint)
        return app

Now I'd like to use pytest to replace the @check_auth decorator with a mock decorator.现在我想使用 pytest 用模拟装饰器替换 @check_auth 装饰器。 After reading these articles: https://medium.com/@hmajid2301/testing-with-pytest-mock-and-pytest-flask-13cd968e1f24阅读这些文章后: https://medium.com/@hmajid2301/testing-with-pytest-mock-and-pytest-flask-13cd968e1f24

Mock authentication decorator in unittesting 单元测试中的模拟身份验证装饰器

I've tried the following methods:我尝试了以下方法:

Method 1 : using pytest-flask and pytest-mock plugins:方法 1 :使用 pytest-flask 和 pytest-mock 插件:

tests/test_annotation_api1.py测试/test_annotation_api1.py

import pytest

from app import create_app

@pytest.fixture
def app(mocker):
    mocker.patch("app.helpers.decorators.check_auth", return_value=True)
    app = create_app()
    return app

def test_get_metric_annotations(client):
    response = client.get(
        '/api/v1/annotation/metric/1',
        content_type='application/json'
    )
    data = response.json
    assert data['status'] == 200

Method 2 : using mock.patch:方法2 :使用mock.patch:

tests/test_annotation_api2.py测试/test_annotation_api2.py

from functools import wraps
from mock import patch

def mock_decorator(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        return f(*args, **kwargs)
    return decorated_function

patch('app.helpers.decorators.check_auth', mock_decorator).start()

from app import create_app

app = create_app()
app.testing = True

def test_get_metric_annotations():
    with app.test_client() as client:
        response = client.get(
            '/api/v1/annotation/metric/1',
            content_type='application/json'
        )
        data = response.json
        assert data['status'] == 200

Method 3 : using the @patch decorator:方法3 :使用@patch 装饰器:

tests/test_annotation_api3.py测试/test_annotation_api3.py

from functools import wraps
from mock import patch

def mock_decorator(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        print("IN HEREEE")
        return f(*args, **kwargs)
    return decorated_function

from app import create_app

app = create_app()
app.testing = True

@patch('app.helpers.decorators.check_auth')
def test_get_metric_annotations(mock_auth):
    mock_auth.return_value = True
    with app.test_client() as client:
        response = client.get(
            '/api/v1/annotation/metric/1',
            content_type='application/json'
        )
        data = response.json
        assert data['status'] == 200

With each of these methods I get the same result, the check_auth decorator fires, ie it isn't being mocked properly:使用这些方法中的每一种我都会得到相同的结果, check_auth 装饰器会触发,即它没有被正确地模拟:

============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.2, py-1.8.1, pluggy-0.13.1
rootdir: /container
plugins: cov-2.8.1, mock-3.1.0, flask-1.0.0
collected 1 item

tests/test_annotation_api.py F                                           [100%]

=================================== FAILURES ===================================
_________________ test_get_metric_annotations_multiple_results _________________

    def test_get_metric_annotations_multiple_results():
        with app.test_client() as client:
            response = client.get(
                '/api/v1/annotation/metric/1',
>               content_type='application/json'
            )


tests/test_annotation_api.py:24: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/usr/local/lib/python3.6/dist-packages/werkzeug/test.py:1006: in get

...
/usr/local/lib/python3.6/dist-packages/flask/views.py:89: in view
    return self.dispatch_request(*args, **kwargs)
/usr/local/lib/python3.6/dist-packages/flask/views.py:163: in dispatch_request
    return meth(*args, **kwargs)
app/helpers/decorators.py:24: in auth_check
    token = get_token_auth_header()

def get_token_auth_header():
        """Obtains the Access Token from the Authorization Header
        """
        auth = request.headers.get("Authorization", None)
        if not auth:
            raise AuthError({"code": "authorization_header_missing",
                            "description":
>                               "Authorization header is expected"}, 401)
E           app.helpers.errorhandlers.AuthError: ({'code': 'authorization_header_missing', 'description': 'Authorization header is expected'}, 401)

app/helpers/jwt_handler.py:16: AuthError

My guess is that I'm not targeting the check_auth decorator properly, but I'm at a loss for what to try next.我的猜测是我没有正确定位 check_auth 装饰器,但我不知道接下来要尝试什么。 Thoughts?想法? Thanks.谢谢。

I figured it out.我想到了。 Following the advice posted here:按照此处发布的建议:

https://stackoverflow.com/a/61289000/1220172 https://stackoverflow.com/a/61289000/1220172

I changed my decorator to:我将装饰器更改为:

app/helpers/decorators.py应用程序/助手/装饰器.py

from functools import wraps

def is_token_valid():
    ''' this will return true if valid, false or raise error otherwise '''
    token = get_token_auth_header() # will get token from Bearer header
    jsonurl = urlopen("https://"+AUTH0_DOMAIN+"/.well-known/jwks.json")
    ...[ pass token to auth0 ]


def check_auth(f):
    @wraps(f)
    def auth_check(*args, **kwargs):
        if is_token_valid() == True:
            return f(*args, **kwargs)
    return auth_check

My test can now mock is_token_valid:我的测试现在可以模拟 is_token_valid:

tests/conftest.py测试/conftest.py

import pytest
from app import create_app

@pytest.fixture
def app(mocker):
    mocker.patch("app.helpers.decorators.is_token_valid", return_value=True)
    app = create_app()
    return app

Hope this helps you if you're having trouble mocking decorators.如果您在 mocking 装饰器中遇到问题,希望这对您有所帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM