3 resuests对象介绍与使用
我们要是用request首先要先下载 requests,我们可以用老办法,通过pip命令下载
>pip install requests
首先我来介绍一下 requests对象的使用。
1) 通过requests发送GET请求。
response = requests.get(url,params=payload)
url为发送的地址,payload为请求的参数,格式为字典类型,前面变量名为params,response为返回变量。
比如:
url =http://www.a.com/user.jsp payload={“id”:”1”,”name”:”Tom”} data = requests.get(url,params=payload)
2) 通过requests发送POST请求。
response = requests.post(url,data=payload)
url为发送的地址,payload为请求的参数,格式为字典类型,前面变量名为data,response为返回变量。
比如:
url =http://www.b.com/login.jsp payload={“username”:”Tom”,”password”:”123456”} data = requests.post(url,data=payload)
3) requests的返回值
这里让我们来讨论下requests的返回值。见表1。
表1:requests的返回值
编号 |
代码 |
解释 |
1 |
response.status_code |
请求页面的状态(状态码) |
2 |
response.headers |
请求网址的headers所有信息 |
3 |
response.cookies |
请求网址的cookies信息 |
4 |
response.url |
请求网址的地址 |
5 |
response.history |
请求的历史记录(以列表的形式显示) |
6 |
response.text |
请求网址的内容信息 |
在这里介绍一下请求页面的状态(状态码),这个在基于HTTP协议的接口测试中经常作为一个验证点。
- 1XX:表示消息
这个比较少用
- 2XX:表示成功
经常使用的是:
200:正确
- #3XX 表示重定向.
经常使用的是:
304: 没有改变
- 4XX 表示客户端错误
经常使用的是:
404: 网址不存在
- 5XX,6XX表示服务器错误.
经常使用的是:
500:服务器内部错误
4)有了上面这些知识,我们来看一下通过request如何来实现接口测试,我们这里以前面介绍的登录模块作为测试对象来设置测试用例。测试用例见表2。
表2:登录模块测试用例
编号 |
用户名 |
密码 |
期望结果 |
1 |
正确 |
错误 |
有提示信息“用户名或者密码错误” |
2 |
错误 |
正确 |
有提示信息“用户名或者密码错误” |
3 |
错误 |
错误 |
有提示信息“用户名或者密码错误” |
4 |
正确 |
正确 |
进入登录后页面,出现“查看购物车” |
假设我们的正确用户名为 jerry,正确密码为000000,这样我们设计测试代码
testLogin.py
import requests #正确的用户名,错误的密码 url=“http://127.0.0.1:8000/login_action/ payload={{"username":"jerry","password":“000000"}} data = requests.post(url,data=payload) if (str(data.status_code)==‘200’) and (“用户名或者密码错误” in str(data.text)) print(“pass”) else: print(“Fail”) #错误的用户名,正确的密码 url=“http://127.0.0.1:8000/login_action/ payload={{"username":“tom","password":“123456"}} data = requests.post(url,data=payload) if (str(data.status_code)==‘200’) and (“用户名或者密码错误” in str(data.text)) print(“pass”) else: print(“Fail”) #错误的用户名,错误的密码 url=“http://127.0.0.1:8000/login_action/ payload={{"username":“tom","password":“000000"}} data = requests.post(url,data=payload) if (str(data.status_code)==‘200’) and (“用户名或者密码错误” in str(data.text)) print(“pass”) else: print(“Fail”) #正确的用户名,正确的密码 url=“http://127.0.0.1:8000/login_action/ payload={{"username":“jerry","password":“123456"}} data = requests.post(url,data=payload) if (str(data.status_code)==‘200’) and (“查看购物车” in str(data.text)) print(“pass”) else: print(“Fail”)
这样的代码虽然可以测试,但是没有测试框架进行限制,代码不利于维护,更不利于批量地执行,我们用刚才介绍的unittest框架进行改造。
testLogin.py
import unittest,requests class mylogin(unittest.TestCase): def setUp(self): print("--------测试开始--------") def test_login_1: url=“http://127.0.0.1:8000/login_action/ payload={{"username":“tom","password":“000000"}} data = requests.post(url,data=payload) self.assertEqual(‘200’,str(data.status_code)) self.assertIn((“用户名或者密码错误”,str(data.text)) def test_login_2: url=“http://127.0.0.1:8000/login_action/ payload={{"username":“jerry","password":“123456"}} data = requests.post(url,data=payload) self.assertEqual(‘200’,str(data.status_code)) self.assertIn((“用户名或者密码错误”,str(data.text)) def test_login_3: url=“http://127.0.0.1:8000/login_action/ payload={{"username":“tom","password":“000000"}} data = requests.post(url,data=payload) self.assertEqual(‘200’,str(data.status_code)) self.assertIn((“用户名或者密码错误”,str(data.text)) def test_login_4: url=“http://127.0.0.1:8000/login_action/ payload={{"username":“jerry","password":“000000"}} data = requests.post(url,data=payload) self.assertEqual(‘200’,str(data.status_code)) self.assertIn((“查看购物车”,str(data.text)) def tearDown(self): print("--------测试结束--------") if __name__=='__main__': #构造测试集 suite=unittest.TestSuite() suite.addTest(mylogin(" test_login_1 ")) suite.addTest(mylogin(" test_login_2 ")) suite.addTest(mylogin(" test_login_3 ")) suite.addTest(mylogin(" test_login_4 ")) #运行测试集合 runner=unittest.TextTestRunner() runner.run(suite)
程序通过self.assertEqual(‘200’,str(data.status_code)),来判断返回码是不是与预期的相同;通过self.assertIn((“用户名或者密码错误”,str(data.text))来判断返回的文本中是不是包括指定的字符串。测试用例test_login_1、test_login_2和test_login_3为错误情况的测试用来,将在返回页面中出现“用户名或者密码错误”的提示,test_login_4为正确的测试用例,登录满足需求,页面跳入到登录商品列表后页面,并且显示“查看购物车”的连接,所以我们以返回页面中是否存在“查看购物车”来判断测试是否成功。
4 数据驱动的自动化接口测试
数据驱动的自动化测试是HP在其著名的产品QTP中进行提出,并且成为了业内自动化测试的一个标准,所谓数据驱动可以理解为测试数据参数化。由于Python读取XML的技术相当成熟,我们可以把测试数据放在XML里来进行设计数据驱动的自动化接口测试。首先来看一下我是如何设计XML文件的。
loginConfig.xml
<node> <case> <TestId>testcase001</TestId> <Title>用户登录</Title> <Method>post</Method> <Desc>正确用户名,错误密码</Desc> <Url>http://127.0.0.1:8000/login_action/</Url> <InptArg>{"username":"jerry","password":"12345"}</InptArg> <Result>200</Result> <CheckWord>用户名或者密码错误</CheckWord> </case> <case> <TestId>testcase002</TestId> <Title>用户登录</Title> <Method>post</Method> <Desc>错误用户名,正确密码</Desc> <Url>http://127.0.0.1:8000/login_action/</Url> <InptArg>{"username":"smith","password":"knyzh158"}</InptArg> <Result>200</Result> <CheckWord>用户名或者密码错误</CheckWord> </case> <case> <TestId>testcase003</TestId> <Title>用户登录</Title> <Method>post</Method> <Desc>错误用户名,错误密码</Desc> <Url>http://127.0.0.1:8000/login_action/</Url> <InptArg>{"username":"smith","password":"12345"}</InptArg> <Result>200</Result> <CheckWord>用户名或者密码错误</CheckWord> </case> <case> <TestId>testcase004</TestId> <Title>用户登录</Title> <Method>post</Method> <Desc>正确用户名,正确密码</Desc> <Url>http://127.0.0.1:8000/login_action/</Url> <InptArg>{"username":"jerry","password":"knyzh158"}</InptArg> <Result>200</Result> <CheckWord>查看购物车</CheckWord> </case> </node>
在这里<node></node>是根标识,<case>…</case>表示一个测试用例,这里面有四个<case>…</case>对,分别上述表示四个测试用例。在<case>…</case>对中,有些数据是为了我们读起来比较方便,有些数据是程序中要是用的,下面来进行分别的介绍。
- <Desc>…</Desc> :测试用例描述
- <Url></Url> :测试的URL地址(程序用到)
- <InptArg>…</InptArg> :请求参数,用{}括起来,为符合Python字典格式的值参对(程序用到)
- <Result>…</Result> :返回码(程序用到)
- <CheckWord>…</CheckWord> :验证字符串(程序用到)
在py文件中我们通过调用from xml.dom import minidom来引入minidom类;dom = minidom.parse('loginConfig.xml')来获取所需要读取的xml文件; root = dom.documentElement来开始获取文件中节点的内容,然后通过语句aaa = root.getElementsByTagName('AAA')来获得文件中的所有叶子节点<AAA>…</AAA>对中的数据,因为文件中有多个<AAA>…</AAA>对,所以返回参数aaa为一个对象列表对,然后通过
for keyin aaa:
aaaValue = key.firstChild.data
print(aaaValue)
来获取每一个<AAA>…</AAA>对中的参数。但是由于XML文件中的标签往往不止一个,且对出现,真像我们文件所以loginConfig.xml中的<TestId>…<TestId>、<Title >…<Title> 、<Method>…</Method> …,所以我们可以这样来获得。
aaa = root.getElementsByTagName('AAA')
bbb = root.getElementsByTagName('BBB')
ccc = root.getElementsByTagName('CCC')
i = 0
for keyin AAA:
aaaValue = aaa[i].firstChild.data
bbbValue = bbb[i].firstChild.data
cccValue = ccc[i].firstChild.data
print(aaaValue)
print(bbbValue)
print(cccValue)
i =i+1
我们来看一下测试代码。
loginConfig.xml
#!/usr/bin/env python #coding:utf-8 import unittest,requests from xml.dom import minidom class mylogin(unittest.TestCase): def setUp(self): print("--------测试结束--------") #从XML中读取数据 dom = minidom.parse('loginConfig.xml') root = dom.documentElement TestIds = root.getElementsByTagName('TestId') Titles = root.getElementsByTagName('Title') Methods = root.getElementsByTagName('Method') Descs = root.getElementsByTagName('Desc') Urls = root.getElementsByTagName('Url') InptArgs = root.getElementsByTagName('InptArg') Results = root.getElementsByTagName('Result') CheckWords =root.getElementsByTagName('CheckWord') i = 0 mylists=[] for TestId in TestIds: mydicts={} #获取每一个数据,形成字典 mydicts["TestId"] = TestIds[i].firstChild.data mydicts["Title"] = Titles[i].firstChild.data mydicts["Method"] = Methods[i].firstChild.data mydicts["Desc"] = Descs[i].firstChild.data mydicts["Url"] = Urls[i].firstChild.data mydicts["InptArg"] = InptArgs[i].firstChild.data mydicts["Result"] = Results[i].firstChild.data mydicts["CheckWord"] =CheckWords[i].firstChild.data mylists.append(mydicts) i = i+1 self.mylists = mylists def test_login(self): for mylist in self.mylists: payload = eval(mylist["InptArg"]) url=mylist["Url"] #发送请求 try: if mylist["Method"] == "post": data = requests.post(url,data=payload) elif mylist["Method"] == "get": data = requests.get(url,params=payload) else: print ("Method 参数获取错误") except Exception as e: self.assertEqual(mylist["Result"],"404") else: self.assertEqual(mylist["Result"],str(data.status_code)) self.assertIn(mylist["CheckWord"],str(data.text)) def tearDown(self): print("--------测试结束--------") if __name__=='__main__': #构造测试集 suite=unittest.TestSuite() suite.addTest(mylogin("test_login")) #运行测试集合 runner=unittest.TextTestRunner() runner.run(suite)
setUp(self)主要把XML里的所有叶子节点数据获取到,放在一个名为mylists的列表变量中,并且返回给self.mylists变量,列表中每一项为一个字典类型的数据,key为XML里的所有叶子节点标签,key所对应的值为XML标签的内容。最后self. mylists传给每个测试函数中使用。
现在我们来看一下函数test_login(self)。
for mylistin self.mylists:把刚才在初始化里面定义的self.mylists每一项分别取出。
payload =eval(mylist["InptArg"]):为获取标签为InptArg中的数据,由于在XML格式定义的时候,这一项用{}括起来,里面是个值参对,由于mylist["InptArg"]返回的是一个{}括起来的具有字典格式的字符串,所以我们必须通过函数eval()进行转移成字典变量赋给payload。
url=mylist["Url"]为发送HTTP的地址。
然后通过判断mylist["Method"]是等于”post”还是等于”get”,选择使用data = requests.post(url,data=payload)或者data =requests.get(url,params=payload)来发送信息,接受信息放在变量data中。
最后通过self.assertEqual(mylist["Result"],str(data.status_code))来判断返回代码是否符合期望结果,以及self.assertIn(mylist["CheckWord"],str(data.text))期望代码mylist["CheckWord"]是否在返回内容str(data.text)中来判断测试是否成功。在这里特别指出在程序中except Exception as e中通过self.assertEqual(mylist["Result"],"404")来判断是否期望结果不存在。在这个项目中我们也加上类似的runtest.py来运行所有的测试用例。格式与前面相同,再次不在重复介绍。图3是加上注册接口测试代码的测试报告。
图3基于Python Requests的HTTP接口测试报告
5 进一步优化
细心的同学可能会发现,上面程序中setUp函数我们可以进行一些封装优化,我们建立一个单独的py文件getXML.py,内容如下:
getXML.py
#!/usr/bin/env python #coding:utf-8 from xml.dom import minidom class GetXML(): def getxmldata(xmlfile): #从XML中读取数据 dom = minidom.parse(xmlfile) root = dom.documentElement TestIds = root.getElementsByTagName('TestId') Titles = root.getElementsByTagName('Title') Methods = root.getElementsByTagName('Method') Descs = root.getElementsByTagName('Desc') Urls = root.getElementsByTagName('Url') InptArgs = root.getElementsByTagName('InptArg') Results = root.getElementsByTagName('Result') CheckWords =root.getElementsByTagName('CheckWord') i = 0 mylists=[] for TestId in TestIds: mydicts={} #获取每一个数据,形成字典 mydicts["TestId"] = TestIds[i].firstChild.data mydicts["Title"] = Titles[i].firstChild.data mydicts["Method"] = Methods[i].firstChild.data mydicts["Desc"] = Descs[i].firstChild.data mydicts["Url"] = Urls[i].firstChild.data mydicts["InptArg"] = InptArgs[i].firstChild.data mydicts["Result"] = Results[i].firstChild.data mydicts["CheckWord"] =CheckWords[i].firstChild.data mylists.append(mydicts) i = i+1 return mylists
这样在loginTest.py改为setUp函数只需要改为:
loginConfig.xml
… from getXML import GetXML #引入刚才建立的类 … class mylogin(unittest.TestCase): def setUp(self): print("--------测试开始--------") self.mylists = GetXML.getxmldata("loginConfig.xml")#调用类中的函数 …