我正在使用燒瓶中的web應用程序,並使用服務層將數據庫查詢和操作從視圖和api路線中提取出來。有人建議,這樣可以讓測試變得更容易,因爲你可以模擬出服務層,但是我很難找出一個好辦法來做到這一點。舉一個簡單的例子,假設我有三個型號的SQLAlchemy:如何在python(flask)webapp中模擬服務層進行單元測試?
models.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,它有一堆功能,如檢查某些用戶或組是否存在,檢查用戶是否是特定組的成員等等。我在api路由中使用這些服務發送JSON的請求,並用它來交易添加到數據庫,類似於這樣:
routes.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 library,和我有補丁,以便從服務模塊的功能,讓他們被重定向到嘲笑,像這樣:
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開發的人通常在測試使用它們的路由時模擬服務?
嗯我正在考慮這樣的事情。爲了澄清一下,傳遞給addtrans路由的'services'是一個模塊?所以我會在routes.py的頂部引入「services」,將它作爲默認關鍵字參數傳遞給每個路由,然後在測試中用模擬覆蓋該關鍵字參數? –
由於模塊是一個「對象」,任何具有正確方法/接口的對象都會這樣做。 – dnozay
太棒了,謝謝! –