我有一个flask应用程序,该应用程序使用auth装饰器来验证对外部服务(auth0)的JWT令牌。该应用程序如下所示:
app / helpers / decorators.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
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
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
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
现在,我想使用pytest用模拟装饰器替换@check_auth装饰器。阅读这些文章后: https://medium.com/@hmajid2301/testing-with-pytest-mock-and-pytest-flask-13cd968e1f24
Mock authentication decorator in unittesting
我尝试了以下方法:
方法1 :使用pytest-flask和pytest-mock插件:
tests / 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
方法2 :使用模拟补丁:
tests / 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
方法3 :使用@patch装饰器:
tests / 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
使用这些方法中的每一个,我都得到相同的结果,将触发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
我的猜测是我没有正确地定位到check_auth装饰器,但是我对下一步的尝试感到迷茫。有什么想法吗?谢谢。
答案 0 :(得分:0)
我知道了。遵循此处发布的建议:
https://stackoverflow.com/a/61289000/1220172
我将装饰器更改为:
app / helpers / decorators.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
我的测试现在可以模拟is_token_valid:
tests / 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
希望在模拟装饰器时遇到问题。