单元测试,主要是为了测试某个方法,或是某个代码快,对于各种输入的处理,输出是否符合预期。但由于其他库、或模块的依赖,以至于很难独立测试我们自己实现的逻辑代码。
对此,引出 mock。
一、Flask
Flask是个轻量 API 框架,使用起来非常容易上手
# 安装:pip install flask
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello Flask'
app.run(port=5000)
这样,一个简单的 server 就跑起来了,访问 http:localhost:5000 便可以看到返回的数据:Hello Flask
下面举例说明,如果对单一的接口写测试用例
二、举例:用户登录
- 用户登录是个常见的功能接口,接口逻辑之外的部分基本同上,这里省略不写。用户使用 name 和 password 进行登陆操作,服务器收到请求后,根据 name 从数据库查询 password ,一致则返回 200 OK,不一致返回 400 Bad Request,很简单的实现,如下:
from flask import request from app.model import UserDB @app.route('/login') def login(): name = request.args.get('name') if not name: return 'name is required', 400 password = request.args.get('password') if not password: return 'password is required', 400 # 从数据库获取用户数据 user = UserDB.get_user(name) if user.get('password') == password: return 'OK', 200 else: return 'password is wrong', 400
其中 UserDB 为数据模块中,从数据库查询用户数据的类。这里对于登录逻辑的单元测试,只指测试该部分最小的代码块,对于代码块中引入的依赖,在测试时都认为是正常的。例如,在测试 login() 的时候,我们认为 UserDB 是正常的、可用的,至于 UserDB 的可靠性,需要 UserDB 模块的单元测试来保障。
对于待测试模块内引入的依赖,采用 mock 的方式模拟。
- Flask 的单元测试,先看代码
此外,还可以对测试缺少参数,这里不再赘述。这样,便可对接口的各种情况进行测试了。import unittest from unittest.mock import Mock from unittest.mock import patch # 该app为创建的Flask实例 from application import app from app.model import UserDB class LoginTestCase(unittest.TestCase): def setUp(self): # push一个上下文,便可以使用flask中的全局变量,如g app.app_context().push() app.testing = True # 测试用的http client self.client = app.test_client() def test_login_success(self): # 真实请求中的url,host和port可省略 url = '/login?name=flask&password=flaskpassword' # 模拟的方法名称,也可直接写字符串: get_user func_name = UserDB.get_user.__name__ # 模拟的方法,不管请求参数是什么,都会返回return_value的值(Mock还有其他用法) mock_func = Mock(return_value={'name': 'flask', 'password': 'flaskpassword'}) # patch意为,当UserDB的get_user方法被调用时,用mock出来的func来处理 # 而mock的func,不管请求参数,都会返回return_value # 故而,只要UserDB的get_user被调用,都会返回{'name': 'flask', 'password': 'flaskpassword'} # with,表示这种处理方式的作用范围 # 当在with的范围之外时,调用UserDB的get_user不受mock影响,会正常调用 with patch.object(UserDB, func_name, func): # response为返回的响应 response = self.client.get(url) # 因为传入的name和password,和UserDB的mock func返回的name和password相同 # 所以,该请求会返回200 # assertEqual意为,认定返回码与200相等,若不等则该用例不通过 self.assertEqual(response.status_code, 200) def test_login_failed(self): # 测试传入错误密码的情况 url = '/login?name=flask&password=wrongpassword' func_name = UserDB.get_user.__name__ mock_func = Mock(return_value={'name': 'flask', 'password': 'flaskpassword'}) with patch.object(UserDB, func_name, func): response = self.client.get(url) # 因为传入密码错误,所以在此我们认定返回码是400 self.assertEqual(response.status_code, 400)
(完)