如何将集成测试应用于 Flask RESTful API

Posted

技术标签:

【中文标题】如何将集成测试应用于 Flask RESTful API【英文标题】:How to apply integration tests to a Flask RESTful API 【发布时间】:2017-06-02 18:28:45 【问题描述】:

[根据https://***.com/a/46369945/1021819,标题应指集成测试而不是单元测试]

假设我想测试以下 Flask API(来自here):

import flask
import flask_restful

app = flask.Flask(__name__)
api = flask_restful.Api(app)

class HelloWorld(flask_restful.Resource):
    def get(self):
        return 'hello': 'world'

api.add_resource(HelloWorld, '/')

if __name__ == "__main__":
    app.run(debug=True)

将其保存为flaskapi.py 并运行它,在同一目录中我运行脚本test_flaskapi.py

import unittest
import flaskapi
import requests

class TestFlaskApiUsingRequests(unittest.TestCase):
    def test_hello_world(self):
        response = requests.get('http://localhost:5000')
        self.assertEqual(response.json(), 'hello': 'world')


class TestFlaskApi(unittest.TestCase):
    def setUp(self):
        self.app = flaskapi.app.test_client()

    def test_hello_world(self):
        response = self.app.get('/')

if __name__ == "__main__":
    unittest.main()

两个测试都通过了,但是对于第二个测试(在TestFlaskApi 中定义)类,我还没有弄清楚如何断言 JSON 响应符合预期(即'hello': 'world')。这是因为它是 flask.wrappers.Response 的一个实例(它可能本质上是一个 Werkzeug 响应对象(参见 http://werkzeug.pocoo.org/docs/0.11/wrappers/)),我还没有找到与 json() 方法等效的 requests Response 对象。

如何对第二个response 的 JSON 内容进行断言?

【问题讨论】:

功能测试怎么样,您不希望单独运行服务以在单独的进程中运行测试吗?你将如何模拟数据库,我认为这应该是通过将对象直接传递给烧瓶来模拟请求的功能测试。 Flask Unittest for Post Method的可能重复 【参考方案1】:

Flask 提供了一个你可以在测试中使用的 test_client:

from source.api import app
from unittest import TestCase

class TestIntegrations(TestCase):
    def setUp(self):
        self.app = app.test_client()

    def test_thing(self):
        response = self.app.get('/')
        assert <make your assertion here>

Flask Testing Docs

【讨论】:

(作为答案发布以帮助其他登陆这里的人看到编写这些测试的更简洁的方法) 干净简洁。 @TheGrimmScientist 提供的文档链接已过时。也许您想将其更新为 flask.palletsprojects.com/en/1.1.x/testing/#the-first-test ?【参考方案2】:

我发现可以通过将json.loads() 应用于get_data() 方法的输出来获取JSON 数据:

import unittest
import flaskapi
import requests
import json
import sys

class TestFlaskApiUsingRequests(unittest.TestCase):
    def test_hello_world(self):
        response = requests.get('http://localhost:5000')
        self.assertEqual(response.json(), 'hello': 'world')


class TestFlaskApi(unittest.TestCase):
    def setUp(self):
        self.app = flaskapi.app.test_client()

    def test_hello_world(self):
        response = self.app.get('/')
        self.assertEqual(
            json.loads(response.get_data().decode(sys.getdefaultencoding())), 
            'hello': 'world'
        )


if __name__ == "__main__":
    unittest.main()

两个测试都按要求通过:

..
----------------------------------------------------------------------
Ran 2 tests in 0.019s

OK
[Finished in 0.3s]

【讨论】:

"状态:Flask API 当前未在积极开发中。"来自github.com/flask-api/flask-api @TheGrimmScientist OP 使用的是 flask-restful,而不是 flask-api【参考方案3】:

你在那里做的不是单元测试。在任何情况下,当使用 requests 库或烧瓶客户端时,您都在做integration testing,因为您对端点进行实际的 http 调用并测试交互。

问题的标题或方法不准确。

【讨论】:

【参考方案4】:

使用 Python3,我收到错误 TypeError: the JSON object must be str, not bytes。需要解码:

# in TestFlaskApi.test_hello_world
self.assertEqual(json.loads(response.get_data().decode()), 'hello': 'world')

This question给出解释。

【讨论】:

【参考方案5】:

来自test_clientresponse 对象有一个get_json 方法。

无需使用json.loads 将响应转换为json。

class TestFlaskApi(unittest.TestCase):
    def setUp(self):
        self.app = flaskapi.app.test_client()

    def test_hello_world(self):
        response = self.app.get("/")
        self.assertEqual(
            response.get_json(),
            "hello": "world",
        )

【讨论】:

以上是关于如何将集成测试应用于 Flask RESTful API的主要内容,如果未能解决你的问题,请参考以下文章

Flask-RESTful中装饰器的使用

接口测试 | 21 基于flask弄个restful API服务出来

如何在 RESTful Flask 应用程序中序列化/反序列化 Pandas DataFrame 到 ProtoBuf/Gzip?

基于 Spring Boot 的微服务集成测试

如何将 flask.url_for() 与 flask-restful 一起使用?

如何用 flask 优雅的实现 restful api