在单元测试中将 JSON 发布到 Flask 端点时出现错误请求 [重复]

Posted

技术标签:

【中文标题】在单元测试中将 JSON 发布到 Flask 端点时出现错误请求 [重复]【英文标题】:Bad Request when posting JSON to Flask endpoint in unit test [duplicate] 【发布时间】:2016-05-11 15:13:48 【问题描述】:

我发现了很多关于人们试图将 JSON 发布到 Flask 应用程序的问题,但这些都不是我在这里遇到的问题。

我有一个简单的 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,所以我将 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',但似乎没有区别。

那么,我的问题: 如何使用Flask.test_client() 在单元测试中将 JSON 发布到端点?我错过了什么明显的东西吗?

【问题讨论】:

也许您的 JSON 格式不正确? (我认为 JSON 需要在单个变量周围使用双引号,而不是单引号。)尝试使用这些值创建一个 python dict,然后在您的 self.app.post() 调用中将 json.dumps(mydict) 作为 data= 参数传递。 是的,这也有效(参见 Martijn 的回复)。我不知何故完全忽略了我的 curl 请求中的数据引用不同(正确)的事实。 【参考方案1】:

您没有发布有效的 JSON:

data="'username': 'jackripper@example.com', 'password': 'crm114'",

注意单引号。 JSON 使用 引号;以下将是有效的 JSON:

data='"username": "jackripper@example.com", "password": "crm114"',

【讨论】:

谢谢!所以,是的......似乎我错过了一些非常明显的东西。我犯了一个错误,只是将现有的 dict 文字括在双引号中。那会教我的。 @tx802:您可以通过在测试中使用json.dumps() 来避免此类错误。 ;-) 我现在肯定会这样做!

以上是关于在单元测试中将 JSON 发布到 Flask 端点时出现错误请求 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

Flask-Security CSRF 令牌

在 Swift 3 中将数据从 JSON 传递到表格视图单元格

JSON 响应断言失败 PHP 单元

如何在单元测试中使用 JSON 发送请求

使用 django 测试客户端发送 JSON

不在节点快递应用程序中将测试文件夹发送到生产环境