flask http-auth 和单元测试
flask http-auth and unittesting
嗨!
我有一个使用 HTTP 基本身份验证保护的路由,它由 Flask-HTTPAuth 实现。如果我使用 curl,一切正常(我可以访问路由),但是在单元测试时,无法访问路由,即使我为它提供了正确的用户名和密码。
以下是我的测试模块中的相关代码片段:
class TestClient(object):
def __init__(self, app):
self.client = app.test_client()
def send(self, url, method, data=None, headers={}):
if data:
data = json.dumps(data)
rv = method(url, data=data, headers=headers)
return rv, json.loads(rv.data.decode('utf-8'))
def delete(self, url, headers={}):
return self.send(url, self.client.delete, headers)
class TestCase(unittest.TestCase):
def setUp(self):
app.config.from_object('test_config')
self.app = app
self.app_context = self.app.app_context()
self.app_context.push()
db.create_all()
self.client = TestClient(self.app)
def test_delete_user(self):
# create new user
data = {'username': 'john', 'password': 'doe'}
self.client.post('/users', data=data)
# delete previously created user
headers = {}
headers['Authorization'] = 'Basic ' + b64encode((data['username'] + ':' + data['password'])
.encode('utf-8')).decode('utf-8')
headers['Content-Type'] = 'application/json'
headers['Accept'] = 'application/json'
rv, json = self.client.delete('/users', headers=headers)
self.assertTrue(rv.status_code == 200) # Returns 401 instead
Flask-HTTPAuth 需要的回调方法如下:
auth = HTTPBasicAuth()
@auth.verify_password
def verify_password(username, password):
# THIS METHOD NEVER GETS CALLED
user = User.query.filter_by(username=username).first()
if not user or not user.verify_password(password):
return False
g.user = user
return True
@auth.error_handler
def unauthorized():
response = jsonify({'status': 401, 'error': 'unauthorized', 'message': 'Please authenticate to access this API.'})
response.status_code = 401
return response
任何我的路线:
@app.route('/users', methods=['DELETE'])
@auth.login_required
def delete_user():
db.session.delete(g.user)
db.session.commit()
return jsonify({})
单元测试抛出以下异常:
Traceback (most recent call last):
File "test_api.py", line 89, in test_delete_user
self.assertTrue(rv.status_code == 200) # Returns 401 instead
AssertionError: False is not true
我想再次强调,当我 运行 curl 使用我为测试客户端提供的完全相同的参数时,一切正常,但是当我 运行 测试时,verify_password 方法甚至没有被调用。
非常感谢您的帮助!
你会喜欢这个的。
你的send
方法:
def send(self, url, method, data=None, headers={}):
pass
你的delete
方法:
def delete(self, url, headers={}):
return self.send(url, self.client.delete, headers)
请注意,您将 headers
作为第三个位置参数传递,因此它将作为 data
进入 send()
。
这是一个如何使用 pytest 和内置的 monkeypatch 夹具完成此操作的示例。
如果我在 some_flask_app
中有这个 API 函数:
from flask_httpauth import HTTPBasicAuth
app = Flask(__name__)
auth = HTTPBasicAuth()
@app.route('/api/v1/version')
@auth.login_required
def api_get_version():
return jsonify({'version': get_version()})
我可以创建一个 return 作为烧瓶测试客户端的装置,并将 HTTPBasicAuth 中的身份验证功能修补为始终 return True
:
import pytest
from some_flask_app import app, auth
@pytest.fixture(name='client')
def initialize_authorized_test_client(monkeypatch):
app.testing = True
client = app.test_client()
monkeypatch.setattr(auth, 'authenticate', lambda x, y: True)
yield client
app.testing = False
def test_settings_tracking(client):
r = client.get("/api/v1/version")
assert r.status_code == 200
嗨!
我有一个使用 HTTP 基本身份验证保护的路由,它由 Flask-HTTPAuth 实现。如果我使用 curl,一切正常(我可以访问路由),但是在单元测试时,无法访问路由,即使我为它提供了正确的用户名和密码。
以下是我的测试模块中的相关代码片段:
class TestClient(object):
def __init__(self, app):
self.client = app.test_client()
def send(self, url, method, data=None, headers={}):
if data:
data = json.dumps(data)
rv = method(url, data=data, headers=headers)
return rv, json.loads(rv.data.decode('utf-8'))
def delete(self, url, headers={}):
return self.send(url, self.client.delete, headers)
class TestCase(unittest.TestCase):
def setUp(self):
app.config.from_object('test_config')
self.app = app
self.app_context = self.app.app_context()
self.app_context.push()
db.create_all()
self.client = TestClient(self.app)
def test_delete_user(self):
# create new user
data = {'username': 'john', 'password': 'doe'}
self.client.post('/users', data=data)
# delete previously created user
headers = {}
headers['Authorization'] = 'Basic ' + b64encode((data['username'] + ':' + data['password'])
.encode('utf-8')).decode('utf-8')
headers['Content-Type'] = 'application/json'
headers['Accept'] = 'application/json'
rv, json = self.client.delete('/users', headers=headers)
self.assertTrue(rv.status_code == 200) # Returns 401 instead
Flask-HTTPAuth 需要的回调方法如下:
auth = HTTPBasicAuth()
@auth.verify_password
def verify_password(username, password):
# THIS METHOD NEVER GETS CALLED
user = User.query.filter_by(username=username).first()
if not user or not user.verify_password(password):
return False
g.user = user
return True
@auth.error_handler
def unauthorized():
response = jsonify({'status': 401, 'error': 'unauthorized', 'message': 'Please authenticate to access this API.'})
response.status_code = 401
return response
任何我的路线:
@app.route('/users', methods=['DELETE'])
@auth.login_required
def delete_user():
db.session.delete(g.user)
db.session.commit()
return jsonify({})
单元测试抛出以下异常:
Traceback (most recent call last):
File "test_api.py", line 89, in test_delete_user
self.assertTrue(rv.status_code == 200) # Returns 401 instead
AssertionError: False is not true
我想再次强调,当我 运行 curl 使用我为测试客户端提供的完全相同的参数时,一切正常,但是当我 运行 测试时,verify_password 方法甚至没有被调用。
非常感谢您的帮助!
你会喜欢这个的。
你的send
方法:
def send(self, url, method, data=None, headers={}):
pass
你的delete
方法:
def delete(self, url, headers={}):
return self.send(url, self.client.delete, headers)
请注意,您将 headers
作为第三个位置参数传递,因此它将作为 data
进入 send()
。
这是一个如何使用 pytest 和内置的 monkeypatch 夹具完成此操作的示例。
如果我在 some_flask_app
中有这个 API 函数:
from flask_httpauth import HTTPBasicAuth
app = Flask(__name__)
auth = HTTPBasicAuth()
@app.route('/api/v1/version')
@auth.login_required
def api_get_version():
return jsonify({'version': get_version()})
我可以创建一个 return 作为烧瓶测试客户端的装置,并将 HTTPBasicAuth 中的身份验证功能修补为始终 return True
:
import pytest
from some_flask_app import app, auth
@pytest.fixture(name='client')
def initialize_authorized_test_client(monkeypatch):
app.testing = True
client = app.test_client()
monkeypatch.setattr(auth, 'authenticate', lambda x, y: True)
yield client
app.testing = False
def test_settings_tracking(client):
r = client.get("/api/v1/version")
assert r.status_code == 200