代码项目实战
文章目录
- 代码项目实战
-
- 面试题:代码实现接口测试和工具实现接口测试的区别?
- 接口自动化框架代码实现顺序
- 1,初始化项目
- 2,初始化日志
- 3,登陆模块接口测试
- 4,员工模块接口测试
-
- 1,新增login.py文件,编写登陆代码并保存全局变量
- 2,封装添加员工接口
- 3, 实现添加员工接口测试用例脚本
- 4, 使用run_suite.py执行员工模块的相关测试用例
- 5,封装查询员工接口的准备
- 6,封装查询员工接口
- 7,实现查询员工接口测试用例
- 8,完成剩余修改和删除员工接口的封装
- 9 完成修改和删除员工接口测试用例
- 10 在run_suite.py中运行,实现员工的增删改查业务场景测试
- 11 参数化员工管理模块
- 12 封装数据库工具类
- 13 在run_suite.py中执行,查看结果
- 14 修改run_suite.py代码,把登陆的测试用例添加到测试套件中
- 15 在run_suite.py中运行登陆和员工模块的测试用例,查看结果
面试题:代码实现接口测试和工具实现接口测试的区别?
优点:
工具:工具使用简单,学习成本低;
代码:灵活,逼格比较高(注意:也可以面试管说逼格比较高。还有面试时,可以夸讲面试官,说他问题问得好)
缺点:
工具:不灵活,
代码:学习成本比较高
什么时候选择工具,什么时候选择代码?
如果项目中,定制化需求比较少,测试人员编码水平不高时,可以选择工具,例如:postman、jmeter等
如果项目中,定制化需求比较多,测试人员编码水平都还可以时,可以选择代码,例如:python+requests+unittest
接口自动化框架代码实现顺序
- 实现顺序:从下往上,从里往外
- 设计顺序:从外到内,从上往下
- 核心功能模块:api,script,data
- 设计顺序:script -> api -> data
- 实现顺序:data -> api -> script
1,初始化项目
创建项目目录结构
安装依赖包
- requests
- pymysql
- parameterzied
- HTMLTestRunner
requests,pymysql,parameterized在File->Setting->Project Intepreter->点击+号 进行安装
2,初始化日志
-
1 编写日志的代码:写在app.py中
-
日志的等级:5个等级(了解)
低于日志等级的日志都不会打印
- DEBUG
- INFO
- WARN
- ERROR
- CRITICAL
-
#app.py 初始化日志的函数代码
# 获取当前项目路径
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
# 初始化日志函数
def init_logging():
# 创建日志器
logger = logging.getLogger()
# 设置日志等级
logger.setLevel(logging.INFO)
# 设置处理器
# 设置控制台处理器
sh = logging.StreamHandler()
# 设置文件处理器
# TimedRotatingFileHandler 可以用来帮助我们切分日志:按照时间来设置日志
filename = BASE_DIR + "/log/ihrm.log"
fh = logging.handlers.TimedRotatingFileHandler(filename, when='M', interval=1, backupCount=7)
# 设置格式化器
fmt = '%(asctime)s %(levelname)s [%(name)s] [%(filename)s(%(funcName)s:%(lineno)d)] - %(message)s'
formatter = logging.Formatter(fmt)
# 将格式化器添加到处理器当中
sh.setFormatter(formatter)
fh.setFormatter(formatter)
# 将处理器添加到日志器
logger.addHandler(fh)
logger.addHandler(sh)
- 2 初始化日志
#api.__init__.py
import app
import logging
# 初始化日志
# 为什么要在api.__init__.py中初始化日志呢?
# 这是因为,我们后面进行接口测试时,都会调用封装的API接口,调用时,会自动运行__init__.py函数,初始化日志器,从而实现,自动初始化日志的功能
app.init_logging()
logging.info("TEST日志器能不能正常工作")
3,登陆模块接口测试
1 封装IHRM登陆接口
- app.py中设置全局属性
#app.py部分代码
HOST = "http://182.92.81.159"
HEADERS = {
"Content-Type":"application/json"}
2 实现IHRM登陆接口测试用例
-
在script中新增test_login.py文件,用于实现登陆接口测试用例
- 先实现第一个测试用例:登陆成功
#scritp.test_login.py import unittest,logging from api.login_api import LoginApi class TestIHRMLogin(unittest.TestCase): def setUp(self) -> None: pass @classmethod def setUpClass(cls) -> None: # 初始化登陆类 cls.login_api = LoginApi() def tearDown(self) -> None: pass @classmethod def tearDownClass(cls) -> None: pass def test01_login_success(self): # 调用封装的登陆接口 response = self.login_api.login('13800000002','123456') # 接收返回的json数据 jsonData = response.json() # 调试输出登陆接口返回的数据,日志输出只能用作为{}占位符 logging.info("登陆成功接口返回的数据为: {}".format(jsonData)) # 断言 self.assertEqual(200, response.status_code) # 断言响应状态码 self.assertEqual(True, jsonData.get("success")) # 断言success self.assertEqual(10000, jsonData.get("code")) # 断言code self.assertIn("操作成功", jsonData.get("message")) # 断言message
3 优化断言
分析登陆成功的代码,发现登陆成功中,断言有4行
# 断言 self.assertEqual(200, response.status_code) # 断言响应状态码 self.assertEqual(True, jsonData.get("success")) # 断言success self.assertEqual(10000, jsonData.get("code")) # 断言code self.assertIn("操作成功", jsonData.get("message")) # 断言message
这4行中可以考虑利用封装技术优化成一行
封装:减少代码冗余,
思路:1.我们的断言代码中,self,response,都是高度重复的代码,可以优化。
2.剩余4个预期结果,都是会动态变化的,所以也可以优化
优化方式:把self,response,4个断言数据作为参数传递给通用断言类处理。
实现代码:通用断言工具类写在utils.py中
# utils.py # 编写断言代码函数 # 可能的问题:self,response为什么可以作为参数传递? # 答:这是因为self,response都是对象 def assert_common(self, response, http_code, success, code, message): self.assertEqual(http_code, response.status_code) # 断言响应状态码 self.assertEqual(success, response.json().get("success")) # 断言success self.assertEqual(code, response.json().get("code")) # 断言code self.assertIn(message, response.json().get("message")) # 断言message
优化后再改写登陆成功案例,至此断言优化完毕
# script.test_login.py import unittest,logging from api.login_api import LoginApi from utils import assert_common class TestIHRMLogin(unittest.TestCase): def setUp(self) -> None: pass @classmethod def setUpClass(cls) -> None: # 初始化登陆类 cls.login_api = LoginApi() def tearDown(self) -> None: pass @classmethod def tearDownClass(cls) -> None: pass def test01_login_success(self): # 调用封装的登陆接口 response = self.login_api.login('13800000002','123456') # 接收返回的json数据 jsonData = response.json() # 调试输出登陆接口返回的数据,日志输出只能用作为{}占位符 logging.info("登陆成功接口返回的数据为: {}".format(jsonData)) # 断言 # self.assertEqual(200, response.status_code) # 断言响应状态码 # self.assertEqual(True, jsonData.get("success")) # 断言success # self.assertEqual(10000, jsonData.get("code")) # 断言code # self.assertIn("操作成功", jsonData.get("message")) # 断言message assert_common(self, response, 200, True, 10000, "操作成功")
4 实现剩余登陆测试用例代码
import unittest,logging from api.login_api import LoginApi from utils import assert_common class TestIHRMLogin(unittest.TestCase): def setUp(self) -> None: pass @classmethod def setUpClass(cls) -> None: # 初始化登陆类 cls.login_api = LoginApi() def tearDown(self) -> None: pass @classmethod def tearDownClass(cls) -> None: pass def test01_login_success(self): # 调用封装的登陆接口 response = self.login_api.login('13800000002','123456') # 接收返回的json数据 jsonData = response.json() # 调试输出登陆接口返回的数据,日志输出只能用作为{}占位符 logging.info("登陆成功接口返回的数据为: {}".format(jsonData)) # 断言 # self.assertEqual(200, response.status_code) # 断言响应状态码 # self.assertEqual(True, jsonData.get("success")) # 断言success # self.assertEqual(10000, jsonData.get("code")) # 断言code # self.assertIn("操作成功", jsonData.get("message")) # 断言message assert_common(self, response, 200, True, 10000, "操作成功") def test02_username_is_not_exist(self): # 调用封装的登陆接口 response = self.login_api.login('13900000002','123456') # 接收返回的json数据 jsonData = response.json() # 输出 logging.info("账号不存在时输出的数据为: {}".format(jsonData)) # 断言 assert_common(self, response, 200, False, 20001, "用户名或密码错误") def test03_password_is_error(self): # 调用封装的登陆接口 response = self.login_api.login('13800000002','error') # 接收返回的json数据 jsonData = response.json() # 输出 logging.info("密码错误时输出的数据为: {}".format(jsonData)) # 断言 assert_common(self, response, 200, False, 20001, "用户名或密码错误") def test04_username_have_special_char(self): # 调用封装的登陆接口 response = self.login_api.login('!@#$%^&*()_-+=','error') # 接收返回的json数据 jsonData = response.json() # 输出 logging.info("账号输入特殊字符时输出的数据为: {}".format(jsonData)) # 断言 assert_common(self, response, 200, False, 20001, "用户名或密码错误") def test05_username_is_empty(self): # 调用封装的登陆接口 response = self.login_api.login('','error') # 接收返回的json数据 jsonData = response.json() # 输出 logging.info("账号为空输出的数据为: {}".format(jsonData)) # 断言 assert_common(self, response, 200, False, 20001, "用户名或密码错误"</