用Python做接口自动化测试

发表于:2017-12-21 10:56

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:XiangXi    来源:博客园

  框架结构如下:
  Test_Api_Project
  |
  |---base.py
  |---base_api
  |   |---register_api.py
  |   |---send_sms_code_api.py
  |---settings.py
  |---test_case
  |   |---test_register_api.py
  |---utilities
  |  |---conn_db.py
  |  |---user.py
  一、先说settings配置文件,与WEB端自动化类似,settings文件中存放整个项目的配置,如:被测项目域名、数据库地址、redis地址、APP版本号、请求头等。
1 # env config
2 ENV = 'test'
3
4 APP_VERSION = '2.3.7'
5
6 HEADERS = {'content-type': 'application/x-www-form-urlencoded; charset=UTF-8'}
7
8 # test url test config
9 API_TEST_BASE_URL = "http://api.abc.com"
10
11 # redis config
12 REDIS_HOST = ''
13 REDIS_PORT = ''
14
15 # mysql config
16 DB_HOST = ''
17 DB_PORT = ''
18 DB_USER = ''
19 DB_PASSWORD = ''
  二、base.py
  该文件中主要是对测试url的处理、对常用的请求类型重新封装(如:GET、POST等)
1 # -*- coding:utf-8 -*-
2 import json,requests
3 import settings
4
5
6 class BaseApi(object):
7     url = ""
8     base_url= settings.API_TEST_BASE_URL
9
10     def __init__(self,url_params=None):
11         if not url_params:
12             url_params = []
13         self.url_params = url_params
14         self.response = None
15         self.base_url = self.base_url
16
17     # 拼接url
18     def api_url(self):
19         if not self.url:
20             raise RuntimeError("no url been set")
21         return self._get_url()
22
23     def _get_url(self):
24         format_url = self.url.format(self.url_params)
25         return "{0}{1}".format(self.base_url, format_url)
26
27     # 封装POST请求类型
28     def post(self, data=None):
29         if not data:
30             data = {}
31         base_param = self.build_base_param()
32         custom_param = self.build_custom_param(data)
33         data.update(base_param)
34         data.update(custom_param)
35         self.response = requests.post(url=self.api_url(), data=data, headers=settings.HEADERS)
36         return self.response
37
38     # 封装GET请求类型
39     def get(self,data=None):
40         if not data:
41             data={}
42         base_param = self.build_base_param()
43         custom_param = self.build_custom_param(data)
44         data.update(base_param)
45         data.update(custom_param)
46         response = requests.get(url=self.api_url(),params=data)
47         return self.response
48
49     # 获取回参中状态码
50     def get_code(self):
51         if self.response:
52             return json.loads(self.response.text)['code']
53
54     # 获取HTTP状态码
55     def get_status_code(self):
56         if self.response:
57             return self.response.status_code
58
59     # 获取回参中message
60     def get_response_message(self):
61         if self.response:
62             return json.loads(self.response.text)['msg']
63
64     # 所有接口共有的入参,比如:app_version、token等
65     def build_base_param(self):
66         return {
67                 "app_version": SETTINGS.APP_VERSION,
68                 "token":""
69         }
70
71     # 被测接口除公共参数外所需的其余参数
72     def build_custom_param(self, data):
73         return {}
  三、conn_db.py 连接数据库
1 import pymysql.cursors,settings
2
3
4 def execute(sql, params=None, db='', is_fetchone=True):
5     # Connect to the database
6     connection = pymysql.connect(host=settings.DB_HOST,
7                                  port=settings.DB_PORT,
8                                  user=settings.DB_USER,
9                                  password=settings.DB_PASSWORD,
10                                  db=db,
11                                  autocommit=True,
12                                  charset='utf8mb4',
13                                  cursorclass=pymysql.cursors.DictCursor)
14     try:
15         with connection.cursor() as cursor:
16             cursor.execute(sql, params)
17             if is_fetchone:
18                 return cursor.fetchone()
19             else:
20                 return cursor.fetchall()
21     finally:
22         connection.close()
  user.py文件主要是对数据库中用户相关的一些操作
  # -*- coding:utf-8 -*-
  import db
  def get_sms_captcha(mobile):
  # 获取短信验证码
  sms_captcha = db.execute('select code from send_sms_code where mobile=%s order by id desc',params=(mobile))
  return sms_captcha['code']
  def delete_user(mobile):
  # 删除用户
  db.execute('delete from user where mobile=%s',params=(mobile))
  四、下面以注册接口为例子
  因注册时需要获取短信验证码,所以除了调用注册接口之外,还需要调用获取短信验证码皆苦,在base_api下新建register_api.py、send_sms_code_api.py,内容如下:
  register_api.py
1 # -*- coding:utf-8 -*-
2 from base_api.base_api import BaseApi
3 import settings
4
5 class RegisterApi(BaseApi):
6     url = '/home/register'
7
8     #对BaseApi类中build_custom_param方法重写
9     def build_custom_param(self, data):
10         return {'login_name':data['login_name'],'password':data['password'],'code':data['code'],'nickname':data['nickname']}
  send_sms_code_api.py
1 # -*- coding:utf-8 -*-
2 from base_api.base_api import BaseApi
3 import settings
4
5
6 class SendSmsCaptcha(BaseApi):
7     url = '/user/sendsms'
8
9     def build_custom_param(self, data):
10         return {'type': data['type'], 'phone': data['phone']}
  对两个接口的url地址和所需要的入参都已经封装好了,接下来开始写case。
  在test_case下新建test_register_api.py
  下面是一个注册成功的例子
1 # -*- coding:utf-8 -*-
2 from unittest import TestCase
3 from base_api.register_api import RegisterApi
4 from base_api.send_sms_code_api import SendSmsCaptcha
5 from utilities import user
6 import settings,json
7
8
9
10 class TestRegisterApi(TestCase):
11     new_mobile = '13000000001'
12     password = '123abc'
13     nick_name = 'XiangXi'
14
15
16     def test_register_success(self):
17         # 调用发送短信验证码接口
18         send_sms_code_api = SendSmsCaptcha()
19         send_sms_code_api.post({'type':'register','phone':self.new_mobile})
20
21         # 校验发送短信验证码接口HTTP状态码为200
22         self.assertEqual(send_sms_code_api.get_status_code(),200)
23
24         # 校验发送短信验证码接口反参中code为0,代表成功(不同项目该字段值不一定为0)
25         self.assertEqual(send_sms_code_api.get_code(),0)
26
27         # 通过数据库获取短信验证码
28         sms_code = user.get_sms_captcha(self.new_mobile)
29
30         # 调用注册接口
31         register_api = RegisterApi()
32         response = register_api.post({'login_name':self.new_mobile,'password':self.password,'code':sms_code,'nickname':self.nick_name})
33
34         # 校验注册接口HTTP状态码为200
35         self.assertEqual(send_sms_code_api.get_status_code(),200)
36
37         # 校验注册接口反参中code为0
38         self.assertEqual(register_api.get_code(),0)
39
40         # 校验反参中手机号、登录名与用户昵称是否与入参值一样
41         identity_obj = json.loads(response.content)['result']['identity_obj']
42         self.assertEqual(identity_obj['nickname'],self.nick_name)
43         self.assertEqual(identity_obj['login_name'],self.new_mobile)
44         self.assertEqual(identity_obj['mobilephone'],self.new_mobile)
45
46     def tearDown(self):
47         user.delete_user(self.new_mobile)
  最后和WEB端类似,通过teardown()方法将新注册的用户在数据库中删除,防止该手机号下次执行case时候报手机号已被注册。
  为了更好了验证通过接口注册的用户信息,也可以调用登录接口测试一下注册接口反参中的用户名是否可以正常登陆返回token。
  五、如果调用接口的前提需要用户登录,那么就需要BaseApi类中build_base_param方法中的token有一个有效的值,否则调用接口时可能会提示token错误或者用户未登录等,这种情况下可以新建一个LoginBaseApi类,继承BaseApi,代码如下:
  假设对登录接口已经进行了封装,入参为login_name和password
1 # -*- coding:utf-8 -*-
2 from base_api.base_api import BaseApi
3 from base_api.login_api import LoginApi
4
5 class LoginBaseApi(BaseApi):
6     def __init__(self, login_name,password, *args, **kwargs):
7         super(LoginBaseApi, self).__init__(*args, **kwargs)
8         self.login_name = login_name
9         self.password = password
10
11     def build_base_param(self):
12         base_param = super(LoginBaseApi, self).build_base_param()
13         response = LoginApi().post(self.login_name, self.password)
14         base_param['token'] = token
15         return base_param
  这时,底层类就有两个,一个是BaseApi() 一个是LoginBaseApi(),需要登录的接口在封装入参时继承LoginBaseApi,不需要登录的接口继承BaseApi
  不同项目接口的入参格式,反参格式可能会不同,仅供参考

上文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理。
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号