知识必备:Python
Api 自动化测试框架 个人源码:https://github.com/wupeng-paynewinn/ApiTest_Unittest 整体:
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 - ApiTest * Common + A.py + BaseTest.py + Cache.py + Decorator.py + General.py + Third.py + __init__.py * Config + config.ini + product-config.ini + test-config.ini * Report + __init__.py + TestReport.html * TestCase + test_01_02_login.py + ... * TestDate + 01_02_login.json + ... * readme.md * requirements.txt * run_all_case.py
一、Config 模块 1 2 3 |-- config.ini |-- product-config.ini |-- test-config.ini
在 config.ini 中的 [ENVIRONMENT] 块中定义环境、发件人、收件人、邮件主题,如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 [ENVIRONMENT] environment =test[SENDER] smtpserver = mail.qq.comuser = xxx@qq.compassword = xxxxxxsender = xxx@qq.com[RECEIVER] receiver = xx@xx.com[MSG] subject = xx自动定时测试报告(生产api)
将会读取 test-config.ini 文件中的配置,由这种方式将配置文件与环境剥离开。
1 2 3 4 5 [HOST] cn_https =https://xx.com.cncn_http =http://xx.com.cnen_https =https://en_http =http://
在 [HOST] 块中定义host,可以在 case.json 文件中使用,如:
1 2 3 4 5 6 7 8 9 10 11 { "request" : { "host" : "{{cn_https}}" , "api" : "/info/api/v3/auth/post/login" , "method" : "POST" , "header" : { "Content-Type" : "application/x-www-form-urlencoded" } } , .... }
使用 {{}} 包裹,将会从config中解析。否则,当成字符串处理
二、case 定义形式 测试用例,以 json 文件的形式定义。主要包括一个 request 块,和多个 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 { "request" : { "host" : "{{cn_https}}" , "api" : "/info/api/v3/auth/post/login" , "method" : "POST" , "header" : { "Content-Type" : "application/x-www-form-urlencoded" } } , "case_01" : { "query" : "test=1&test2=2" , "data" : [ { "key" : "useragent" , "value" : "android" , "type" : "text" } ] , "assert" : [ { "type" : "eq" , "rules" : "status" , "expect" : 0 , "msg" : "这里是报错显示的msg" } ] , "set_result" : [ { "key" : "cache_key" , "value" : "payload.id" } ] } }
request 对象中包含:
host:在config 中定义
api:接口地址,与host组成完整url
method:请求方式,支持POST与GET
header:对象,可填写多个键值对
case_01 为用例名称,一个json文件中可以含有多个用例。同一个文件下所有的用例,请求的内容都相同。
query: 字符串,按照”field1=value1&field2=value2”规则,解析结果将于data合并
data: 数组,按照method内容,post则放在body中发送,get则放在url中。数组中可放入多个对象。
key: 必填,参数名称。
value:必填,请求内容。
type:必填,类型:text/array/json/file
file_name: 非必填,type 为 file 时需要参数。文件名称。
file_type: 非必填,type 为 file 时需要参数。文件类型。例子:text/plain
from:非必填,数据来源。当数据来源于cache时,填cache。目前只支持cache
assert:数组,定义请求结果断言。可以含有多个对象,定义不同的断言,
type:断言类型,eq/not_eq等
rules:取值规则,返回数据中的结构 例如 payload.group_id
expect: 预期值,对比用
set_result:非必填 数组,需要保存在cache中的数据
key: 名称,注意唯一性
result_field:返回数据的结构 例如:payload.group_id
三、定义单元测试 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 import unittestfrom Common.BaseTest import BaseTestclass TestLogin (BaseTest ): json_file = "/TestData" def test_01_login (self ): self.run_case("case_01" ) def test_02_login (self ): self.run_case("case_02" ) self.set_result("286592399" ) def test_03_create_group (self ): self.set_json_file("/TestData" ) self.set_single_extra_body("code_list" , "1112,1113" ) self.set_single_extra_body("code" , "1200" ) self.run_case("case_01" ) if __name__ == '__main__' : unittest.main()
四、Cache 模块 为了方便用例间上下文联动,引入cache模块。可以将接口返回的结果保存在cache中,也可以从cache中取出所需要的内容放入到请求体中。
但需要注意的是,cache模块受用例执行顺序的影响,必须保证当前取值的用例在存值用例的后面执行。
使用:
五、给case请求体增加参数
增加 header
set_extra_header
1 2 self.set_extra_header({"test_header" :"header" ,"test_data2" :"header" })
set_single_extra_header
1 self.set_single_extra_header(key="test_header" ,value="header" )
增加 body(post):
将在body体中传输
set_extra_body
1 self.set_extra_body({"test_data" :"data" ,"test_data1" :"data" })
set_single_extra_body
1 self.set_single_body(key="test_data" ,value="1" )
增加 params (get):
将在url中传输
六、执行顺序问题 在 run_all_case.py 文件中定义测试用例执行:
1 2 3 4 5 6 7 suite = unittest.TestSuite() suite.addTest(TestVanishLogin('test_1_login_01' )) suite.addTest(TestVanishLogin('test_1_login_02' )) suite.addTest(TestVanishLogin('test_2_create_01' )) runner = unittest.TextTestRunner(verbosity=2 ) runner.run(suite)
使用 addTest 手动增加测试用例,设置执行顺序。在执行用例很多的情况下,比较麻烦,也可以使用:
1 2 3 4 discover = unittest.defaultTestLoader.discover(test_dir, pattern='test_*.py' ) runner = HTMLTestRunner(stream=fp, title=u'测试报告' , description=u'用例执行情况:' ) runner.run(discover)
使用自动 discover 功能,自动加载所有测试用例,但其加载执行的顺序是按照文件名和方法名排序的。
需要优先执行的文件可以将文件名命名为: test_1_1_1_vanish_account_login.py
文件内需要优先执行的用例可以将方法名命名为: test_1_1_account_login_01()
用名称中的数字用来控制用例执行顺序。
运行用例、取最新测试报告并发送邮件 需要导入python的HTMLTestRunner第三方模块
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 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 # -*- coding: utf-8 -*- import unittest from HTMLTestRunner import HTMLTestRunner import time import os import smtplib from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart from email.header import Header # import logging # from tool import output_log import urllib3 urllib3.disable_warnings() run_os = 'linux' # 2.定义:取最新测试报告 def new_file(test_dir): # 列举test_dir目录下的所有文件,结果以列表形式返回。 lists = os.listdir(test_dir) # sort按key的关键字进行排序,lambda的入参fn为lists列表的元素,获取文件的最后修改时间 # 最后对lists元素,按文件修改时间大小从小到大排序。 if run_os == 'linux': lists.sort(key=lambda fn: os.path.getmtime(test_dir + '/' + fn)) # linux else: lists.sort(key=lambda fn: os.path.getmtime(test_dir + '\\' + fn)) # windows # 获取最新文件的绝对路径 file_path = os.path.join(test_dir, lists[-1]) return file_path # 3.定义:发送邮件,发送最新测试报告html def send_email(newfile): try: # 打开文件 f = open(newfile, 'rb') # 读取文件内容 mail_body = f.read() # 关闭文件 f.close() # 发送邮箱服务器 import configparser if run_os == 'linux': config_file = os.path.abspath('.') + '/Config/config.ini' # linux 运行全部接口 else: config_file = os.path.abspath('.') + '\\Config\\config.ini' # windows 运行全部接口 # print "config_file :" + str(config_file) config = configparser.ConfigParser() config.read_file(open(config_file, encoding='UTF-8')) user = config.get("SENDER", "user") password = config.get("SENDER", "password") smtpserver = config.get("SENDER", "smtpserver") sender = config.get("SENDER", "sender") receiver = config.get("RECEIVER", "receiver") subject = config.get("MSG", "subject") # 发送邮件主题 # subject = '自动定时测试报告(生产api)'+now msg = MIMEMultipart('mixed') msg_html1 = MIMEText(mail_body, 'html', 'utf-8') msg.attach(msg_html1) msg_html = MIMEText(mail_body, 'html', 'utf-8') msg_html["Content-Disposition"] = 'attachment; filename="TestReport.html"' msg.attach(msg_html) msg['From'] = sender # 多个收件人 msg['To'] = receiver msg['Subject'] = Header(subject, 'utf-8') # 连接发送邮件 # 2.7版本不需要往SMTP()里面加实参 smtp = smtplib.SMTP_SSL(smtpserver) smtp.connect(smtpserver, 465) #smtp.ehlo() # smtp.starttls() smtp.login(user, password) smtp.sendmail(sender, receiver, msg.as_string()) smtp.quit() except Exception as e: print(e) # 4.查找报告中是否有failtest(ft),find找不到则返回-1 def parse_html(new_report): from bs4 import BeautifulSoup with open(new_report, 'r', encoding='utf-8') as wb_data: soup = BeautifulSoup(wb_data, 'html.parser') return str(str(soup).find('id="ft')) if __name__ == '__main__': try: # 1.执行测试用例,生成最新的测试用例 now = time.strftime('%Y-%m-%d_%H_%M_%S') test_dir = os.path.abspath('./TestCase') print(test_dir) discover = unittest.defaultTestLoader.discover(test_dir, pattern='test_*.py') # 测试报告的路径 if run_os == 'linux': test_report_dir = os.path.abspath('.') + '/Report' # linux filename = test_report_dir + '/' + now + '_result.html' else: test_report_dir = os.path.abspath('.') + '\\Report' # windows filename = test_report_dir + '\\' + now + '_result.html' fp = open(filename, 'wb') runner = HTMLTestRunner(stream=fp, title=u'测试报告', description=u'用例执行情况:') runner.run(discover) fp.close() # 2.取最新测试报告 new_report = new_file(test_report_dir) print("测试报告路径:%s" % new_report) # 3.如果有运行失败的case,发送邮件,发送最新测试报告html if parse_html(new_report) != "-1": send_email(new_report) print("Email sent successfully...") elif parse_html(new_report) == "-1": print("All passed, no need to send email...") except Exception as e: print(e)
Unittest、Github、Jenkins持续化集成 一、Jenkins服务搭建 1、linux搭建jenkins 1 2 3 4 5 war部署: 1.下载安装包jenkins.war; 2.在安装包根路径下,运行命令 java -jar jenkins.war --httpPort=8080 3.打开浏览器进入链接 http://ip地址:8080 4.填写初始密码,激活系统
1 2 #默认密码: cat /var/lib/jenkins/secrets/initialAdminPassword
1 2 3 4 5 6 7 8 9 docker部署: 下载镜像:docker pull jenkins/jenkins 查看本地镜像:docker images 镜像实例化: mkdir -p /var/jenkins_node chmod -R 777 /var/jenkins_node docker run -d -uroot -p 80:8080 --name jenkins -v /var/jenkins_node:/var/jenkins_home jenkins/jenkins 查看容器是否运行:docker ps 进入容器:docker exec -it -uroot jenkins /bin/bash
2、linux搭建Git环境 1 2 3 4 5 6 7 yum install git #安装git git version #验证是否安装 mkdir /xx/xx #创建目录 cd /xx/xx git init #初始化仓库 git remote add origin +地址 #连接到git git pull origin +分支 #拉取代码
二、构建项目 1、配置Git代码拉取 1 2 3 源码管理:选择项目源码地址, 选择git,输入仓库地址, 然后添加Credentials用户校验
2、设置定时任务 构建触发器选择:定时构造(下面是15分钟运行一次) 每行包含5个字段,依次为分钟、小时、日、月、星期几
3、构建 1 2 3 cd /home/paynewinn/ApiTest_Chinamobile git pull --rebase origin master python3 /home/paynewinn/ApiTest_Chinamobile/run_all_case.py
完成后点击应用即可