FastAPI(八十四)实战开发《在线课程学习系统》--接口测试
我们在之前讲了单元测试,这次我们分享下一部分接口的接口测试。
那么我们看来看下对应的接口怎么测试。
那么我们去设计对应的登陆用例,由于接口我们之前开发过,我们参照之前的接口开发的代码进行用例设计。
1.登陆用户不存在。
输入参数:
1 2 3 4 | self .parame = { "username" : "liwanle1i33333" , "password" : "123456" } |
期望:
1 2 | message = '用户不存在' code = 100205 |
2.登陆成功
参数:
1 2 3 4 | self .parame = { "username" : "liwanle1i" , "password" : "123456" } |
预期:
1 2 | message = '成功' code = 200 |
3.密码正确再次登陆直接返回之前的token,预期还是一样的。
4.输入密码错误
参数:
1 2 3 4 | self .parame = { "username" : "liwanle1i" , "password" : "123452336" } |
预期:
1 2 | code = 100206 message = '密码错误' |
我们设计了四条登陆相关的case,那么对应的完整的case
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | import unittest, requests class UserLoginCase(unittest.TestCase): @classmethod def setUpClass( cls ) - > None : cls .url = 'http://127.0.0.1:8000/user/login' @classmethod def tearDownClass( cls ) - > None : ''' 还原测试环境,测试url :return: ''' cls .client = None cls .url = '' def setUp( self ) - > None : ''' 初始化参数 :return: ''' self .parame = { "username" : "liwanle1i" , "password" : "123456" } def tearDown( self ) - > None : '''最后清理参数''' self .parame.clear() def test_login_usernot_exict( self ): parame = { "username" : "liwanle1i33333" , "password" : "123456" } reponse = requests.post( self .url, json = parame) status = reponse.status_code reslut = reponse.json() self .assertEqual(status, 200 ) self .assertEqual(reslut[ 'code' ], 100205 ) self .assertEqual(reslut[ 'message' ], '用户不存在' ) def test_login_success( self ): reponse = requests.post( self .url, json = self .parame) status = reponse.status_code reslut = reponse.json() self .assertEqual(status, 200 ) self .assertEqual(reslut[ 'code' ], 200 ) self .assertEqual(reslut[ 'message' ], '成功' ) def test_login_success_two( self ): reponse = requests.post( self .url, json = self .parame) status = reponse.status_code reslut = reponse.json() self .assertEqual(status, 200 ) self .assertEqual(reslut[ 'code' ], 200 ) self .assertEqual(reslut[ 'message' ], '成功' ) def test_login_error( self ): self .parame[ 'password' ] = '2222222222' reponse = requests.post( self .url, json = self .parame) status = reponse.status_code reslut = reponse.json() self .assertEqual(status, 200 ) self .assertEqual(reslut[ 'message' ], "密码错误" ) if __name__ = = "__main__" : unittest.main() |
执行结果:
我们来看下我们的测试用例,目前少了二个场景,密码错误超过10次但是时间小于30min,一个是时间大于30min,那么我们如何实现呢,最简单的就是for循环10次,其实这样做不可以,因为没有了,那么应该怎么做呢,我们可以直接这么来做。直接修改redis即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | def test_log_error_big( self ): red = redis.Redis(host = 'localhost' , port = 6379 , db = 0 ) red.hset( "liwanle1i_password" , 'num' , 11 ) red.hset( "liwanle1i_password" , 'time' , "2021-11-17 22:16:57" ) self .parame[ 'password' ] = '2222222222' reponse = requests.post( self .url, json = self .parame) status = reponse.status_code print (reponse.text) reslut = reponse.json() self .assertEqual(status, 200 ) self .assertEqual(reslut[ 'code' ], 100204 ) self .assertEqual(reslut[ 'message' ], "输入密码错误次数过多,账号暂时锁定,请30min再来登录" ) red.hdel( "liwanle1i_password" , 'num' ) def test_log_error_bigtime( self ): red = redis.Redis(host = 'localhost' , port = 6379 , db = 0 ) red.hset( "liwanle1i_password" , 'num' , '1' ) red.hset( "liwanle1i_password" , 'time' , "2021-10-17 22:16:57" ) self .parame[ 'password' ] = '2222222222' reponse = requests.post( self .url, json = self .parame) status = reponse.status_code print (reponse.text) reslut = reponse.json() print (reslut) self .assertEqual(status, 200 ) self .assertEqual(reslut[ 'message' ], "密码错误" ) red.hdel( "liwanle1i_password" , 'time' ) def test_log_error_bigtime_success( self ): red = redis.Redis(host = 'localhost' , port = 6379 , db = 0 ) red.hset( "liwanle1i_password" , 'num' , '1' ) red.hset( "liwanle1i_password" , 'time' , "2021-10-17 22:16:57" ) reponse = requests.post( self .url, json = self .parame) status = reponse.status_code print (reponse.text) reslut = reponse.json() print (reslut) self .assertEqual(status, 200 ) self .assertEqual(reslut[ 'message' ], "成功" ) red.hdel( "liwanle1i_password" , 'time' ) red.hdel( "liwanle1i_password" , 'num' ) |
我们还在增加了一个当有错误密码,但是次数不大于10,时间大于30min的可以正常登陆成功,不过我门也发现了接口存在问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | @usersRouter .post( "/login" ) async def login(request: Request, user: UserLogin, db: Session = Depends(get_db)): db_crest = get_user_username(db, user.username) if not db_crest: logger.info( "login:" + user.username + "不存在" ) return reponse(code = 100205 , message = '用户不存在' , data = "") verifypassowrd = verify_password(user.password, db_crest.password) if verifypassowrd: useris = await request.app.state.redis.get(user.username) if not useris: try : token = create_access_token(data = { "sub" : user.username}) except Exception as e: logger.exception(e) return reponse(code = 100203 , message = '产生token失败' , data = '') request.app.state.redis. set (user.username, token, expire = ACCESS_TOKEN_EXPIRE_MINUTES * 60 ) return reponse(code = 200 , message = '成功' , data = { "token" : token}) return reponse(code = 200 , message = '成功' , data = { "token" : useris}) else : result = await request.app.state.redis.hgetall(user.username + "_password" , encoding = 'utf8' ) if not result: times = datetime.strftime(datetime.now(), "%Y-%m-%d %H:%M:%S" ) request.app.state.redis.hmset_dict(user.username + "_password" , num = 0 , time = times) return reponse(code = 100206 , data = ' ', message=' 密码错误') else : errornum = int (result[ 'num' ]) numtime = (datetime.now() - datetime.strptime(result[ 'time' ], '%Y-%m-%d %H:%M:%S' )).seconds / 60 if errornum < 10 and numtime < 30 : # 更新错误次数 errornum + = 1 request.app.state.redis.hmset_dict(user.username + "_password" , num = errornum) return reponse(code = 100206 , data = ' ', message=' 密码错误') elif errornum < 10 and numtime > 30 : # 次数置于1,时间设置现在时间 errornum = 1 times = datetime.strftime(datetime.now(), "%Y-%m-%d %H:%M:%S" ) request.app.state.redis.hmset_dict(user.username + "_password" , num = errornum, time = times) return reponse(code = 100206 , data = ' ', message=' 密码错误') elif errornum > 10 and numtime < 30 : # 次数设置成最大,返回 errornum + = 1 request.app.state.redis.hmset_dict(user.username + "_password" , num = errornum) return reponse(code = 100204 , message = '输入密码错误次数过多,账号暂时锁定,请30min再来登录' , data = '') else : errornum = 1 times = datetime.strftime(datetime.now(), "%Y-%m-%d %H:%M:%S" ) request.app.state.redis.hmset_dict(user.username + "_password" , num = errornum, time = times) return reponse(code = 100206 , data = ' ', message=' 密码错误') |
我们的接口测试就是在不断的去发现问题改造问题。
这里提供了一个demo,所有的代码都放在了
1 | https: / / gitee.com / liwanlei / fastapistuday |
用例设计是门艺术,我们在不断的测试中调整去修改优化我们的代码。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· 展开说说关于C#中ORM框架的用法!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?