UnitTest测试框架
一、requests模块
Ⅰ、 模块导入
requests模块非原生模块,所以要使用需要先进行安装,cmd中输入命令:pip install requests
① 如果是已经安装过的则展示安装所在的文件位置
② 如果安装完成后PyCharm中导入模块import requests
标红报错
解决办法1:(可解决当前项目 import requests 问题,但新建一个项目可能又不行)
- 打开pycharm->File->Settings->Project Interprete ,点击“+”号按钮
- 然后点击添加 ,搜requests,选中后点左下角的install ,等待安装完成即可,就不会报ModuleNotFoundError: No module named 'requests’这个错了
解决办法2:(新建一个项目也没问题) https://blog.csdn.net/qq_21041889/article/details/94392538
Ⅱ 、发送请求 / 获取响应结果
发送请求:
reques t[ rɪ'kwest ]
r = requests.方法(url, headers,data, ···)
其中url必填,其他参数根据实际情况选择性使用,发送请求后悔获取响应结果,然后把结果赋值给变量,最后通过变量的属性值取出需要的结果
获取结果:
response [ rɪ'spɒns ]
response.headers 获取返回的头信息
response.text 获取返回的主体
response.cookies 获取返回的cookie
response.status_code 获取返回的状态码
···
实操:
import requests
# 访问网易首页,并获取返回状态码
test_url = "http://www.163.com"
r = requests.get(test_url)
print(r.status_code)
# 输出
200
Ⅲ、常用参数
url:
唯一必填参数,上方有说明
headers:
模拟手机端访问网易首页,这时候就需要带上headers参数,通过抓包可发现headers有个字段"User-Agent",而服务器就是通过这个字段来判断访问的来源,代码如下:
import requests
# 访问网易首页,并获取返回状态码
test_url = "http://www.163.com"
h = {
"User-Agent":"Android/BKL-AL00/9.0.0/"}
r = requests.get(test_url, header = h)
print(r.status_code)
cookies:
cookies参数以字典形式发送,只需要对应的将name
和value
传入即可,代码如下:
import requests
test_url = "http://mail.163.com/js6/main.jsp?sid=xkjskjsnlsoakskoa&df=163nav_icon"
c = "JSESSIONID=6272e7c3762fa26619780af615a75a0b"
h = {
"User-Agent":"Android/BKL-AL00/9.0.0/"}
r = requests.get(test_url, header = h, cookies = c)
print(r.status_code)
params: 可以存放请求的表单,并会以key1=value1&key2=value2的形式跟在url之后发送,为了区分url和参数,最好不要把表单放在url中,可以通过params参数来分离
import requests
# 分离 (test_url = "http://mail.163.com/js6/main.jsp?sid=xkjskj&df=163nav_icon")
test_url = "http://mail.163.com/js6/main.jsp"
p = ("sid":"xkjskj", "df":"163nav_icon") # 将需要发送的表单以字典的形式赋值到变量p中
h = {
"User-Agent":"Android/BKL-AL00/9.0.0/"}
r = requests.get(test_url, params = p)
print(r.status_code)
data: 此参数也是用于存放请求的表单,是request模块最重要的参数之一
补充:
params和data的区别:
params > params是添加到url的请求字符串中的,用于get请求,只能提交一种类型的数据"字符串"
data > 添加到请求体(body)中的, 用于post请求
post请求: 可以提交4种类型的数据,取决与服务器接收的数据类型,post的数据类型需要和服务器接收的一致,不然服务器就无法正确识别post的数据,导致测试结果报错。
- post四种数据类型:
- application/json:
Content-Type:application/json
作为响应头大家肯定不陌生,现在越来越多的人把它作为请求头,用来告诉服务端消息主体是序列化后的 JSON 字符串。 - application/x-www-form-urlencoded
- multipart/form-data
- text/xml
- application/json:
Ⅳ、porttest
- 请求简介:
- 不带params的get请求,一般是请求静态页面,大多数只用来测试页面是否存在,返回数据是否正常
- 带params的get请求,一般是查询请求
- 带form的post请求,一般是修改/提交/查询…数据
二、有效可用调试接口
`https接口`
淘宝查询电话号码归属地(可用)
https://tcc.taobao.com/cc/json/mobile_tel_segment.htm?tel=手机号
百付宝接口(可用)
https://www.baifubao.com/callback?cmd=1059&callback=phone&phone=手机号
`http接口`
快递接口:
http://www.kuaidi100.com/query?type=快递公司代号&postid=快递单号
ps:快递公司编码:申通=”shentong” EMS=”ems” 顺丰=”shunfeng” 圆通=”yuantong” 中通=”zhongtong”
韵达=”yunda” 天天=”tiantian” 汇通=”huitongkuaidi” 全峰=”quanfengkuaidi” 德邦=”debangwuliu” 宅急送=”zhaijisong”
谷歌接口(不确定是否可用)
FeedXml转json接口:
http://ajax.googleapis.com/ajax/services/feed/load?q=Feed地址&v=1.0
备选参数:callback:&callback=foo就会在json外面嵌套foo({
})方便做jsonp使用。
备选参数:n:返回多少条记录。
音乐接口
QQ空间音乐接口(可用)
http://qzone-music.qq.com/fcg-bin/cgi_playlist_xml.fcg?uin=QQ号码&json=1&g_tk=1916754934
QQ空间收藏音乐接口(可用)
http://qzone-music.qq.com/fcg-bin/fcg_music_fav_getinfo.fcg?dirinfo=0&dirid=1&uin=QQ号&p=0.519638272547262&g_tk=1284234856
多米音乐接口(可用)
http://v5.pc.duomi.com/search-ajaxsearch-searchall?kw=关键字&pi=页码&pz=每页音乐数
soso接口(可用)
http://cgi.music.soso.com/fcgi-bin/fcg_search_xmldata.q?source=10&w=关键字&perpage=1&ie=utf-8
地图接口
阿里云根据地区名获取经纬度接口(可用)
http://gc.ditu.aliyun.com/geocoding?a=苏州市
参数解释: 纬度,经度type 001 (100代表道路,010代表POI,001代表门址,111可以同时显示前三项)
阿里云根据经纬度获取地区名接口(可用)
http://gc.ditu.aliyun.com/regeocoding?l=39.938133,116.395739&type=001
IP接口
新浪接口(ip值为空的时候 获取本地的)(可用)
http://int.dpool.sina.com.cn/iplookup/iplookup.php?format=json&ip=218.4.255.255
手机信息查询接口
淘宝网接口(可用)
http://tcc.taobao.com/cc/json/mobile_tel_segment.htm?tel=手机号
获取QQ昵称和用户头像(可用,不过会提示登录)
http://r.qzone.qq.com/cgi-bin/user/cgi_personal_card?uin=QQ
三、断言方法汇总
assertEqual:A为预期值,B为实际值
方法 | 解释 |
---|---|
assertEqual(a, b) | 判断ab是否相等 |
assertNotEqual(a, b) | 判断ab不相等 |
assertIs(a, b) | 判断a是b |
assertIsNot(a, b) | 判断a不是b |
assertIsNone(a) | 判断a是不是None |
assertIsNotNone(a) | 判断a不是None |
assertIn(a,b) | 判断a在b中,此时a与b可以相等 |
assertNotIn(a, b) | 判断a不在b中 |
assertIsInstance(a, b) | 判断a是否属于b的实例 |
assertNotIsInstance(a, b) | 判断a不属于b的实例 |
assertGreater(a, b) | 判断a > b |
assertGreaterEqual(a, b) | 判断a >= b |
assertLess(a, b) | 判断a < b |
assertLessEqual(a, b) | 判断a <= b |
四、UnitTest测试框架
- UnitTest
Unit :单元 [ 'juːnɪt ]
- testcase(测试用例)
- testsuite(测试套件):多个测试用例组成一个测试套件
- testfixtrue(测试固件):整合了代码中相同的公共部分,减少代码冗余,比如通过
setup()
进行初始化,同样的还可以使用teardown()
来结束测试工作 - testrunner(测试运行器):给测试用例提供运行环境,通过它的
run()
方法,来执行测试用例,并在执行完成后把结果输出在testresult
中
· HTMLTestRunner(测试报告)
Ⅰ、testcase(测试用例)
需要通过继承TestCase类来构建测试用例,既可以一个测试用例生产一个类,也可以多个测试用例生产一个类,后者执行效率更高效,构建代码格式如下:
calss 测试类名(unittest.TestCase): # 这个类继承了unittest的TestCase的基类
testcase1
testcase2
...
实际代码:
import unittest
import requests
class VersionTest(unittest.TestCase):
def test1(self):
url = "https://jdsq.jdsq360.com/wx/version/check"
form = ({
"version": "1.1.6", "type": "vp"})
r = requests.post(url, data=form)
self.assertEqual(r.text, "验证成功!") # r.text返回主体
def test2(self):
url = "https://jdsq.jdsq360.com/wx/version/check"
form = ({
"version": "1.1.7", "type": "vp"})
r = requests.post(url, data=form)
self.assertEqual(r.text, "验证失败!")
Ⅱ、testsuite(测试套件)
完成了 testcase准备工作,进行组合时就需要用到testsuite suite:套 [ swiːt ]
import unittest
import requests
class VersionTest(unittest.TestCase):
def test1(self):
# 省略之前代码
def test2(self):
# 省略之前代码
# 1
def suite():
vt = unittest.TestSuite() # TestSuite重点,勿写错!!!!!
vt.addTest(VersionTest("test1")) # 把"test1"的用例都导入套件
vt.addTest(VersionTest("test2")) # 把"test2"的用例都导入套件
return vt
# 2
def suite():
vt = unittest.makeSuite(VersionTest, "test") # 把所有"test"开头的用例都导入套件
return vt
Ⅲ、test runner(测试运行器)
import unittest
import requests
class VersionTest(unittest.TestCase):
def test1(self):
# 省略之前代码
def test2(self):
# 省略之前代码
# 1
def suite():
# 省略之前代码
if __name__ == '__main__':
# 方法1
# 运行套件suite()中的testcase
unittest.TextTestRunner().run(suite())
# verbosity 参数可以控制输出的错误报告的详细程度,不填写默认为1,详情见补充内容
unittest.TextTestRunner(verbosity=2).run(suite())
# 方法2
unittest.main() # 所有操作封装在main方法中,完成所有testcase的加载和运行
补充verbosity 参数内容:
0 (静默模式): 你只能获得总的测试用例数和总的结果 比如 总共100个 失败20 成功80
1 (默认模式): 非常类似静默模式 只是在每个成功的用例前面有个“.” 每个失败的用例前面有个 “F”
2 (详细模式):测试结果会显示每个测试用例的所有相关的信息
Ⅳ、HTMLTestRunner(测试报告)
下载地址:http://tungwaiyip.info/software/HTMLTestRunner.html
此模板基于python 2.x写的,如果使用python 3.x的话需要做以下修改:
94行: 引入的名称,从import StringIO
改成import io
539行: self.outputBuffer=StringIO.StringIO()
改成self.outputBuffer=io.StringIO()
631行: print>>sys.stderr,'\nTime Elapsed:%s' % (self.stopTime-self-startTime)
改成:
print(sys.stderr, '\nTime Elapsed: %s' % (self.stopTime-self.startTime))
642行: if not rmap.has_key(cls):
改成if not cls in rmap:
766行: uoo.decode('latin-1')
改成uo = e
772行: ue = e.decode('latin-1')
改成ue = e
778行: output = saxutils.escape(uo+ue),
改成output = saxutils.escape(str(uo)+str(ue)),
存储路径:
将修改的文件存放在Pythonx.x安装路径Lib文件夹下
尝试运行:
import unittest
import requests
import HTMLTestRunner
class VersionTest(unittest.TestCase):
def test1(self):
# 省略之前代码
def test2(self):
# 省略之前代码
# 1
def suite():
# 省略之前代码
if __name__ == '__main__':
fr = open("testreal.html", "wb") # 新建一个testreal.html文件,并设置读写权限为读写
runner = HTMLTestRunner.HTMLTestRunner(stream=fr, title="测试报告", description="详情")
runner.run(suite())
# 使用HTMLTestRunner模块中的HTMLTestRunner方法,构建一个运行器对象,并通过参数将结果写入testreal.html文件
# 标题为"测试报告",描述为"详情",最后也是通过run()方法完成运行
五、实际问题汇总
Ⅰ、测试报告不生成
1、无报错:
PyCharm会默认使用自带的unittest框架来执行单元测试,不会执行main函数中的代码,所以不生成测试报告 ,判断可查看右上角文件名前有 Unittests in 文件名
解决:
2、有报错:
报错信息:.<_io.TextIOWrapper name=’’ mode=‘w’ encoding=‘UTF-8’>
解决:HTMLTestRunner.py
文件修改631行
,1或者2修改为3保存即可解决
1、print >> sys.stderr, ‘\nTime Elapsed: %s’ % (self.stopTime-self.startTime)
2、print(sys.stderr, ‘\nTime Elapsed: %s’ % (self.stopTime-self.startTime))
3、sys.stderr.write(’\nTime Elapsed: %s\n’ % (self.stopTime - self.startTime))
Ⅱ、处理josn返回数据
1、转换为字典
import json
AA = {
"statusCode": 200,
"data": {
"totoal": "5",
"height": "5.97",
"weight": "10.30",
"age": "11"
},
"msg": "成功"
}
s = json.dumps(AA) # dumps:把字典AA转换为json字符串
s1 = json.loads(s) # loads:把json转换为dict
print(s1["statusCode"]) # 打印statusCode对应的值 200
print(s1["data"]["age"]) # 打印AA里 data下age对应的值 11
print(len(s1["data"])) # 打印AA里 data对应的值的个数 4
print(len(s1)) # 打印AA里值对的个数 3
import json
def test3(self):
test_url = "https://xxx.goho.co/jdshop/api/wxapp/store/getBanner"
r = requests.get(test_url)
s1 = json.loads(r.text)
self.assertEqual(len(s1), 3) # 判断数量是否为3
# json.loads()返回josn数据主体转换为python字典,并赋值给s1
# r.text 接口返回主体(是josn数据格式)
# len(s1)获取字典内元素个数
Ⅲ、处理返回的cookies
response.cookies是获取response中cookie属性,返回的<class ‘requests.cookies.RequestsCookieJar’>,是一个类
response.cookies.get_dict()返回的是字典格式cookie
Eg:
import requests
url = "https://xxx.jdsq360.com/api/store/storer/login"
p = {
"account": "wanglixin", "pwd": "123456"}
r = requests.get(url, params=p)
cookies = r.cookies.get_dict()
print(r.cookies)
print(cookies)
# 输出结果
<RequestsCookieJar[<Cookie JSESSIONID=B889AF415023FFD986C8E389CD9DFD58 for haohuo.jdsq360.com/>]>
{
'JSESSIONID': 'B889AF415023FFD986C8E389CD9DFD58'}
Ⅳ、生成测试报告报错
1、 报错信息:<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'>
解决方案:
HTMLTestRunner.py的631行,此时可能是1或者2,修改为3
1.print >> sys.stderr, '\nTime Elapsed: %s' % (self.stopTime-self.startTime)
2.print(sys.stderr, '\nTime Elapsed: %s' % (self.stopTime-self.startTime))
3.sys.stderr.write('\nTime Elapsed: %s\n' % (self.stopTime - self.startTime))
<<< 修改为此内容即可
2、 报错信息:requests.exceptions.SSLError: HTTPSConnectionPool
解决方案1:抓包工具忘记关windows抓包导致证书错误,关闭即可.