在单元测试中将 JSON 发布到 Flask 端点时出现错误请求
Bad Request when posting JSON to Flask endpoint in unit test
我发现了很多关于人们试图 post JSON Flask 应用程序的问题,但其中 none 正是我在这里遇到的问题。
我有一个简单的 REST API,它是使用 Flask 和 Flask-JWT 构建的,它在浏览器中运行良好,但我的单元测试遇到了障碍。该应用程序是 App Engine 应用程序,可以在本地开发服务器和 App Engine 上正常运行。我 运行 我的单元测试是 python -m unittest <module>
。
我有一个使用简单工厂创建的基本 Flask 应用程序:
def create_app(name=__name__, cfg):
app = Flask(name)
app.config.update(cfg)
CORS(app)
JWT(app, auth_handler, identity_handler)
@app.route('/api/v1/ping', methods=['GET'])
def handle_ping():
return jsonify({'message': 'pong'})
# other routes ...
在上面,JWT
需要一个 authentication_handler
,我已经实现了:
def auth_handler(identity, secret):
# validate identity and secret
return User(...)
我的客户是 Angular.js 所以我 posting JSON 到这个端点。这在浏览器或 curl:
中都可以正常工作
$ curl -H "content-type:application/json" -d '{"username":"jackripper@example.com","password":"crm114"}' http://localhost:8080/api/v1/auth
在响应中给我一个 200 OK
和一个访问令牌。
我的简单测试用例是这样的:
conf = {
'DEBUG': False,
'TESTING': True,
'SECRET_KEY': 'MoveAlongTheresNothingToSeeHere',
'JWT_AUTH_URL_RULE': '/api/v1/auth'
}
class FlaskRouteTests(unittest.TestCase):
def setUp(self):
api = factory.create_app(__name__, conf)
self.app = api.test_client()
def tearDown(self):
pass
def testRoute(self):
# This test works just fine
resp = self.app.get('/api/v1/ping')
self.assertEqual(resp.status, "200 OK", "Status should be 200, was %s" % resp.status)
def testAuth(self):
# This test does not work
resp = self.app.post('http://localhost:8080/api/v1/auth',
data="{'username': 'jackripper@example.com', 'password': 'crm114'}",
content_type='application/json', charset='UTF-8')
self.assertEqual(resp.status, "200 OK", "Status should be 200, was %s" % resp.status)
普通的旧 GET
测试 (testRoute()
) 工作得很好,但 testAuth()
给了我一个 400 Bad Request
,我不知道为什么。
如果我在 werkzeug environ
将请求发送到我的 Flask 应用程序之前查看它,我会看到:
{
'SERVER_PORT': '8080',
'SERVER_PROTOCOL': 'HTTP/1.1',
'SCRIPT_NAME': '',
'wsgi.input': <_io.BytesIO object at 0x111fa8230>,
'REQUEST_METHOD': 'POST',
'HTTP_HOST': 'localhost:8080',
'PATH_INFO': '/api/v1/auth',
'wsgi.multithread': False,
'QUERY_STRING': '',
'HTTP_CONTENT_TYPE': 'application/json',
'HTTP_CONTENT_LENGTH': '53',
'CONTENT_LENGTH': '53',
'wsgi.version': (1, 0),
'SERVER_NAME': 'localhost',
'wsgi.run_once': False,
'wsgi.errors': <open file '<stderr>', mode 'w' at 0x10fd2d1e0>,
'wsgi.multiprocess': False,
'flask._preserve_context': False,
'wsgi.url_scheme': 'http',
'CONTENT_TYPE': u'application/json'
}
因此,内容类型设置正确,如果我阅读 wsgi.input
(BytesIO.getvalue()
) 的内容,我会看到 {'username': 'jackripper@example.com', 'password': 'crm114'}
请求似乎在某处失败之前它甚至击中了我的auth_handler
,所以在 werkzeug 或 Flask 的某处。
- werkzeug
EnvironBuilder
将数据解释为 str
并将其转换为 BytesIO
流 - 我不知道这是否符合预期。
- 我试过将数据作为字符串发布:
data="{'username': 'jackripper@example.com', 'password': 'password'}"
和作为字典:data={'username': 'jackripper@example.com', 'password': 'password'}
,但似乎没有区别。
那么,我的问题:
我如何 post JSON 使用 Flask.test_client()
到单元测试中的端点?我是否漏掉了一些明显的东西?
您发布的内容无效 JSON:
data="{'username': 'jackripper@example.com', 'password': 'crm114'}",
注意单引号。 JSON 使用 双 引号;以下是有效的 JSON:
data='{"username": "jackripper@example.com", "password": "crm114"}',
我发现了很多关于人们试图 post JSON Flask 应用程序的问题,但其中 none 正是我在这里遇到的问题。
我有一个简单的 REST API,它是使用 Flask 和 Flask-JWT 构建的,它在浏览器中运行良好,但我的单元测试遇到了障碍。该应用程序是 App Engine 应用程序,可以在本地开发服务器和 App Engine 上正常运行。我 运行 我的单元测试是 python -m unittest <module>
。
我有一个使用简单工厂创建的基本 Flask 应用程序:
def create_app(name=__name__, cfg):
app = Flask(name)
app.config.update(cfg)
CORS(app)
JWT(app, auth_handler, identity_handler)
@app.route('/api/v1/ping', methods=['GET'])
def handle_ping():
return jsonify({'message': 'pong'})
# other routes ...
在上面,JWT
需要一个 authentication_handler
,我已经实现了:
def auth_handler(identity, secret):
# validate identity and secret
return User(...)
我的客户是 Angular.js 所以我 posting JSON 到这个端点。这在浏览器或 curl:
中都可以正常工作$ curl -H "content-type:application/json" -d '{"username":"jackripper@example.com","password":"crm114"}' http://localhost:8080/api/v1/auth
在响应中给我一个 200 OK
和一个访问令牌。
我的简单测试用例是这样的:
conf = {
'DEBUG': False,
'TESTING': True,
'SECRET_KEY': 'MoveAlongTheresNothingToSeeHere',
'JWT_AUTH_URL_RULE': '/api/v1/auth'
}
class FlaskRouteTests(unittest.TestCase):
def setUp(self):
api = factory.create_app(__name__, conf)
self.app = api.test_client()
def tearDown(self):
pass
def testRoute(self):
# This test works just fine
resp = self.app.get('/api/v1/ping')
self.assertEqual(resp.status, "200 OK", "Status should be 200, was %s" % resp.status)
def testAuth(self):
# This test does not work
resp = self.app.post('http://localhost:8080/api/v1/auth',
data="{'username': 'jackripper@example.com', 'password': 'crm114'}",
content_type='application/json', charset='UTF-8')
self.assertEqual(resp.status, "200 OK", "Status should be 200, was %s" % resp.status)
普通的旧 GET
测试 (testRoute()
) 工作得很好,但 testAuth()
给了我一个 400 Bad Request
,我不知道为什么。
如果我在 werkzeug environ
将请求发送到我的 Flask 应用程序之前查看它,我会看到:
{
'SERVER_PORT': '8080',
'SERVER_PROTOCOL': 'HTTP/1.1',
'SCRIPT_NAME': '',
'wsgi.input': <_io.BytesIO object at 0x111fa8230>,
'REQUEST_METHOD': 'POST',
'HTTP_HOST': 'localhost:8080',
'PATH_INFO': '/api/v1/auth',
'wsgi.multithread': False,
'QUERY_STRING': '',
'HTTP_CONTENT_TYPE': 'application/json',
'HTTP_CONTENT_LENGTH': '53',
'CONTENT_LENGTH': '53',
'wsgi.version': (1, 0),
'SERVER_NAME': 'localhost',
'wsgi.run_once': False,
'wsgi.errors': <open file '<stderr>', mode 'w' at 0x10fd2d1e0>,
'wsgi.multiprocess': False,
'flask._preserve_context': False,
'wsgi.url_scheme': 'http',
'CONTENT_TYPE': u'application/json'
}
因此,内容类型设置正确,如果我阅读 wsgi.input
(BytesIO.getvalue()
) 的内容,我会看到 {'username': 'jackripper@example.com', 'password': 'crm114'}
请求似乎在某处失败之前它甚至击中了我的auth_handler
,所以在 werkzeug 或 Flask 的某处。
- werkzeug
EnvironBuilder
将数据解释为str
并将其转换为BytesIO
流 - 我不知道这是否符合预期。 - 我试过将数据作为字符串发布:
data="{'username': 'jackripper@example.com', 'password': 'password'}"
和作为字典:data={'username': 'jackripper@example.com', 'password': 'password'}
,但似乎没有区别。
那么,我的问题:
我如何 post JSON 使用 Flask.test_client()
到单元测试中的端点?我是否漏掉了一些明显的东西?
您发布的内容无效 JSON:
data="{'username': 'jackripper@example.com', 'password': 'crm114'}",
注意单引号。 JSON 使用 双 引号;以下是有效的 JSON:
data='{"username": "jackripper@example.com", "password": "crm114"}',