FastAPI(八十四)实战开发《在线课程学习系统》--接口测试
我们在之前讲了单元测试,这次我们分享下一部分接口的接口测试。
那么我们看来看下对应的接口怎么测试。
那么我们去设计对应的登陆用例,由于接口我们之前开发过,我们参照之前的接口开发的代码进行用例设计。
1.登陆用户不存在。
输入参数:
self.parame = { "username": "liwanle1i33333", "password": "123456" }
期望:
message='用户不存在' code=100205
2.登陆成功
参数:
self.parame = { "username": "liwanle1i", "password": "123456" }
预期:
message='成功' code=200
3.密码正确再次登陆直接返回之前的token,预期还是一样的。
4.输入密码错误
参数:
self.parame = { "username": "liwanle1i", "password": "123452336" }
预期:
code=100206 message='密码错误'
我们设计了四条登陆相关的case,那么对应的完整的case
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即可。
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的可以正常登陆成功,不过我门也发现了接口存在问题。
@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,所有的代码都放在了
https://gitee.com/liwanlei/fastapistuday
用例设计是门艺术,我们在不断的测试中调整去修改优化我们的代码。