如何在 Flask-RESTful 的 Resource 类中为函数进行单元测试?

6

我对单元测试和RESTful API开发都比较陌生。我想知道如何对Flask Restful中的Resource类中的函数进行单元测试?我可以对端点的响应进行单元测试,但是我不知道如何对端点控制器类内的单个函数进行测试。

下面是我的应用程序代码。它包括3个文件,包括测试:

  • api.py
  • controller_foo.py
  • test_controller_foo.py
# api.py

from flask import Flask
from flask_restful import Api

from .controller_foo import ControllerFoo


def create_app(config=None):
    app = Flask(__name__)
    app.config['ENV'] ='development'
    return app

application = app = create_app()
api = Api(app)

api.add_resource(ControllerFoo, '/ctrl')

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

# controller_foo.py

from flask_restful import Resource
from flask import request

class ControllerFoo(Resource):
    """
    basically flask-restful's Resource method is a wrapper for flask's MethodView
    """
    def post(self):
        request_data = self.handle_request()
        response = self.process_request(request_data)
        return response

    def handle_request(self):
        json = request.get_json()
        return json

    def process_request(self, data):
         # do some stuffs here
         return {'foo': 'bar'}

我正在使用 unittest

# test_controller_foo.py

import unittest

from api import app
from .controller_foo import ControllerFoo

# initiating class to try testing but I don't know how to start
ctrl = ControllerFoo()

class ControllerFooTestCase(unittest.TestCase):
    def setUp(self):
        self.app = app
        self.app.config['TESTING'] = True
        self.client = app.test_client()
        self.payload = {'its': 'empty'}

    def tearDown(self):
        pass

    def test_get_response(self):
        response = self.client.post('/ctrl', json=self.payload)
        expected_resp = {
            'foo': 'bar'
        }
        self.assertEqual(response.status_code, 200)
        self.assertDictEqual(response.get_json(), expected_resp)

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

我想知道如何正确地为handle_requestprocess_request函数进行单元测试。

编辑:修复我的有错误的代码。感谢Laurent LAPORTE的指导。


1
不确定为什么被踩了。如果这不是一个有效的问题,请告诉我。 - addicted
我想更新一下,我设法自己找到了问题的答案。为了对handle_requestprocess_request函数进行单元测试,我可以使用```with self.app.test_request_context('/ctrl'): # code here 要测试ControllerFoo类内部的函数,我可以创建该类的实例,然后直接调用函数。目前这种方法满足我的需求。 - addicted
1个回答

5

你的代码中有几个bug,所以很难解释清楚。

首先,在使用Flask(以及Flask-Restful)进行测试时,推荐使用PyTest而不是unittest,因为它更容易设置和使用。

请参阅文档:测试Flask应用程序

但是,你可以从unittest开始...

注意:app模块和该模块中的app实例之间可能会混淆。因此,为了避免这种情况,我导入了该模块。另一个好习惯是将测试模块命名为被测试模块的名称:"app.py"=>"test_app.py"。你也可能会混淆controller模块和controller实例。最佳做法是使用更精确的名称,例如"controller_foo"或其他名称...

这里是一个工作正常的单元测试:

# test_app.py

import unittest

import app


class ControllerTestCase(unittest.TestCase):
    def setUp(self):
        self.app = app.app
        self.app.config['TESTING'] = True
        self.client = self.app.test_client()
        self.payload = {'its': 'empty'}

    def test_get_response(self):
        response = self.client.post('/ctrl', json=self.payload)
        expected_resp = {'foo': 'bar'}
        self.assertEqual(response.status_code, 200)
        self.assertDictEqual(response.get_json(), expected_resp)


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

你可以看到,我还修复了你的应用程序中发布的URL链接,URL是"/ctrl",而不是"controller"。
到这个步骤,测试可以运行,但是你会遇到另一个错误:
Ran 1 test in 0.006s

FAILED (errors=1)

Error
Traceback (most recent call last):
  ...
TypeError: process_request() takes 1 positional argument but 2 were given

如果你查看你的 process_request() 方法,你会发现你遗漏了 self 参数。请将它更改如下。
    def process_request(self, data):
        # do some stuffs here
        return {'foo': 'bar'}

你的测试应该通过。

但是,那不是实现 Flask-Restful 控制器的正确方式。请阅读文档并使用getpost方法……


啊,你说得对,我漏掉了很多细节。我正在重新编写代码,以遮盖我的真实代码,并可能在此过程中错过了一些细节。感谢你指出这一点! - addicted
除了错误之外,我仍然想为ControllerFoo类中的辅助函数进行单元测试。我在这里阅读了文档,但它没有关于抽象HTTP方法内部步骤的示例。我的方法内有更多逻辑,我想使用函数作为抽象层。 - addicted

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接