我正在使用烧瓶中的 web 应用程序,并使用服务层将数据库查询和操作从视图和 api 路由中抽象出来。有人建议这使测试更容易,因为您可以模拟服务层,但我无法找到一个好的方法来做到这一点。作为一个简单的例子,假设我有三个 SQLAlchemy 模型:
模型.py
class User(db.Model):
id = db.Column(db.Integer, primary_key = True)
email = db.Column(db.String)
class Group(db.Model):
id = db.Column(db.Integer, primary_key = True)
name = db.Column
class Transaction(db.Model):
id = db.Column(db.Integer, primary_key = True)
from_id = db.Column(db.Integer, db.ForeignKey('user.id'))
to_id = db.Column(db.Integer, db.ForeignKey('user.id'))
group_id = db.Column(db.Integer, db.ForeignKey('group.id'))
amount = db.Column(db.Numeric(precision = 2))
有用户和群组,以及用户之间的交易(代表货币易手)。现在我有一个services.py,它有很多功能,例如检查某些用户或组是否存在、检查用户是否是特定组的成员等。我在发送 JSON 的 api 路由中使用这些服务在请求中并使用它向数据库添加事务,类似于以下内容:
路线.py
import services
@app.route("/addtrans")
def addtrans():
# get the values out of the json in the request
args = request.get_json()
group_id = args['group_id']
from_id = args['from']
to_id = args['to']
amount = args['amount']
# check that both users exist
if not services.user_exists(to_id) or not services.user_exists(from_id):
return "no such users"
# check that the group exists
if not services.group_exists(to_id):
return "no such group"
# add the transaction to the db
services.add_transaction(from_id,to_id,group_id,amount)
return "success"
当我尝试模拟这些服务进行测试时,问题就来了。我一直在使用模拟库,我不得不修补服务模块中的函数,以便将它们重定向到模拟,如下所示:
mock = Mock()
mock.user_exists.return_value = True
mock.group_exists.return_value = True
@patch("services.user_exists",mock.user_exists)
@patch("services.group_exists",mock.group_exists)
def test_addtrans_route(self):
assert "success" in routes.addtrans()
出于各种原因,这感觉很糟糕。一、修补感觉脏;第二,我不喜欢单独修补我正在使用的每个服务方法(据我所知,没有办法修补整个模块)。
我已经想到了几种解决方法。
- 重新分配 routes.services 以便它引用我的模拟而不是实际的服务模块,例如:
routes.services = mymock
- 让服务成为作为关键字参数传递给每个路由的类的方法,并在测试中简单地传递我的模拟。
- 与 (2) 相同,但使用单例对象。
我无法评估这些选项并考虑其他选项。在测试使用它们的路由时,做 python web 开发的人通常如何模拟服务?