如何解决如何在flask / flask-restx中测试采用解析器输入的GET方法?
我正在使用Flask-restx制作Flask应用程序,并通过request parsing从用户那里获取输入,如下所示:
from flask_restx import Resource,reqparse
from .services.calculator import DimensionCalculator
parser = reqparse.RequestParser()
parser.add_argument("dimensions",type=float,required=True,action='split',help="Dimensions of the rectangle (in meters)")
parser.add_argument("angle_inclination",action='append',help="Angle of inclination of the Dachfläche (Neigung)")
@ns.route("/output")
class UserOutput(Resource):
@ns.expect(parser,validation=True)
def get(self):
args = parser.parse_args()
return DimensionCalculator.inputs(**args)
其中ns
是我定义的名称空间,DimensionCalculator.inputs
的简化版本是:
class DimensionCalculator:
def inputs(**user_input):
installation_place = user_input['installation_place']
num_rectangles = user_input['num_rectangles']
dimensions = user_input['dimensions']
angle_inclination = user_input['angle_inclination']
alignment = user_input['alignment']
direction = user_input['direction']
vendor = user_input['vendor']
output = {
"installation_place": installation_place,"num_rectangles": num_rectangles,"area_shape": area_shape,"vendor": vendor
}
return output
我正在使用pytest编写测试。我已经为所有类和方法编写了测试,而我唯一无法测试的是GET
中定义的UserOutput
方法。有没有办法测试GET
方法?
感谢您的帮助。
解决方法
考虑单元测试标记,我将介绍我想出的关于如何完全隔离地测试它的内容。基本上,get
方法对依赖项进行了两次函数调用,因此从单元意义上讲,您必须检查是否确实进行了这些调用,并确定了参数,对吗?
该示例的项目结构:
+---Project
| |
| | __init__.py
| | config.py
| | dimension_calculator.py
| | parser_impl.py
| | user_output.py
| | user_output_test.py
因此,为简单起见,一切都是平坦的。
最重要的是,您必须将UserOutput
模块与依赖项分离。您不应该对这样的依赖项进行硬编码:
from .services.calculator import DimensionCalculator
假设,DimensionCalculator
可能包含复杂的业务逻辑,不应包含在测试范围内。因此,UserOutput
模块的外观如下:
from flask_restx import Resource,Api
from flask import Flask
from .config import Config
app = Flask(__name__)
api = Api(app)
ns = api.namespace('todos',description='TODO operations')
@ns.route("/output")
class UserOutput(Resource):
@ns.expect(Config.get_parser_impl(),validation=True)
def get(self):
args = Config.get_parser_impl().parse_args()
return Config.get_dimension_calculator_impl().inputs(**args)
if __name__ == '__main__':
app.run(debug=True)
如您所见,“外部”依赖项现在可以轻松地存根(这是称为“ 依赖项注入”的常见模式的一部分)。 Config模块如下所示:
from .parser_impl import parser
from .dimension_calculator import DimensionCalculator
class Config(object):
parser_impl = parser
calculator = DimensionCalculator
@staticmethod
def configure_dimension_calculator_impl(impl):
Config.calculator = impl
@staticmethod
def configure_parser_impl(impl):
Config.parser_impl = impl
@staticmethod
def get_dimension_calculator_impl():
return Config.calculator
@staticmethod
def get_parser_impl():
return Config.parser_impl
最后但并非最不重要的地方是我们将依赖项存根并注入它们的地方:
from .user_output import UserOutput
from flask import Flask
from .config import Config
class ParserStub(object):
parse_args_call_count = 0
@staticmethod
def parse_args():
ParserStub.parse_args_call_count = ParserStub.parse_args_call_count + 1
return {'parser_stub': 2}
class DimensionCalculatorStub(object):
inputs_call_count = 0
@staticmethod
def inputs(**args):
DimensionCalculatorStub.inputs_call_count = DimensionCalculatorStub.inputs_call_count + 1
return {'stub': 1}
app = Flask(__name__)
def test_user_request_get():
with app.test_request_context():
# given
Config.configure_dimension_calculator_impl(DimensionCalculatorStub)
Config.configure_parser_impl(ParserStub)
uo = UserOutput()
# when
uo.get()
# then
assert DimensionCalculatorStub.inputs_call_count == 1
assert ParserStub.parse_args_call_count == 1
# assert arguments as well!
在我的情况下,测试通过了。缺少的一件事是参数验证。
为了完整起见,我还将包括DimensionCalculator和解析器本身,尽管它们与您的示例完全相同。我只对它们进行了模块化:
from flask_restx import reqparse
parser = reqparse.RequestParser()
parser.add_argument("dimensions",type=float,required=True,action='split',help="Dimensions of the rectangle (in meters)")
parser.add_argument("angle_inclination",action='append',help="Angle of inclination of the Dachfläche (Neigung)")
和dimension_calculator.py:
class DimensionCalculator:
@staticmethod
def inputs(**user_input):
installation_place = user_input['installation_place']
num_rectangles = user_input['num_rectangles']
dimensions = user_input['dimensions']
angle_inclination = user_input['angle_inclination']
alignment = user_input['alignment']
direction = user_input['direction']
vendor = user_input['vendor']
output = {
"installation_place": installation_place,"num_rectangles": num_rectangles,"area_shape": "EMPTY","vendor": vendor
}
return output
重要肯定有用于此类存根/模拟的准备和配置的专用框架(例如:https://pypi.org/project/pytest-mock/)。我只想介绍这个概念和最简单的方法。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。