---Web自动化测试之Webdriver(python)--从零到熟练(系列)
6.6 编写自动化测试用例
我们规划好了代码架构,就可以编写具体的测试用例了。首先,我们写登录的测试用例,在写自动化测试用例的时候,我们要先写一下公用函数类。根据需要,我们写了三个通用的类放到CommonFunction文件夹下:WebDriverHelp用来存放所有页面操作用到公用方法;QT_Operations 用来存放具体操作功能块,如登录,退出等;DataOperations用来存放所有数据操作,用来读取xml中的测试数据。
然后我们在TestCases文件夹下编写登录的测试用例TestCase_QT_Login.py执行具体的测试操作,验证测试用例执行是否成功。
并把测试用例需要的测试数据,放到TestData文件夹下TestCase_QT_Login.xml文件中。如果网站有所变化,可以修改这个里面的测试数据,从而减少代码维护的成本。
6.6.1 WebDriverHelp类的内容
WebDriverHelp是我们所有测试用例能用到的方法,可以在编写测试过程中不断抽象出来,写到这个类里面,相当于对Webdriver再次做了一下封装。
下面是部分代码,以供大家参考:
@author:songxianfeng
@copyright: V1.0
'''
import time
import datetime
from selenium import webdriver
from selenium.webdriver.support.ui import Select
from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
global G_WEBDRIVER, G_BROWSERTYTPE,DRIVER
class WebDriverHelp(object):
'''
本类主要完成页面的基本操作,如打开指定的URL,对页面上在元素进行操作等
'''
def __init__(self,btype="close",atype="firefox",ctype="local"):
'''
根据用户定制,打开对应的浏览器
@param bType: 开关参数,如果为close则关闭浏览器
@param aType:打开浏览器的类型,如chrome,firefox,ie等要测试的浏览器类型
@param cType:打开本地或是远程浏览器: local,本地;notlocal:远程 '''
global DRIVER
if( btype == "open" ):
if( atype == "chrome" ):
if(ctype == "local"):
DRIVER = webdriver.Chrome()
DRIVER.maximize_window()
elif(ctype == "notlocal"):
DRIVER = webdriver.Remote(command_executor='http://124.65.151.158:4444/wd/hub', desired_capabilities=webdriver.DesiredCapabilities.CHROME)
DRIVER.maximize_window()
elif( atype == "ie" ):
if(ctype == "local"):
DRIVER = webdriver.Ie()
DRIVER.maximize_window()
elif(ctype == "notlocal"):
DRIVER = webdriver.Remote(command_executor='http://124.65.151.158:4444/wd/hub',desired_capabilities=webdriver.DesiredCapabilities.INTERNETEXPLORER)
DRIVER.maximize_window()
elif( atype == "firefox" ):
if(ctype == "local"):
DRIVER = webdriver.Firefox()
DRIVER.maximize_window()
elif(ctype == "notlocal"):
DRIVER = webdriver.Remote(command_executor='http://10.20.5.56:4444/wd/hub',desired_capabilities=webdriver.DesiredCapabilities.FIREFOX)
DRIVER.maximize_window()
self.DRIVER = DRIVER
def setup(self,logintype):
'''
定制测试URL
@param loginplace: 指定测试的URL: qiantai:前台测试地址,houtai:后台测试地址,zhengshi:正式环境测试地址
ysh:原始会测试地址 zhengshiysh:正式原始会测试地址
'''
try:
qiantai_url = "http://test.zhongchou.cn"
ysh_url = "http://test.ysh.zhongchou.cn"
houtai_url = "http://test.admin.zhongchou.cn"
zhengshi_url = "http://www.zhongchou.cn"
zhengshi_ysh_url = "http://ysh.zhongchou.cn"
if(logintype=="qiantai"):
self.DRIVER.get(qiantai_url)
elif(logintype=="houtai"):
self.DRIVER.get(houtai_url)
elif(logintype=="zhengshi"):
self.DRIVER.get(zhengshi_url)
elif(logintype=="ysh"):
self.DRIVER.get(ysh_url)
elif(logintype=="zhengshiysh"):
self.DRIVER.get(zhengshi_ysh_url)
else:
print '路径错误!'
self.DRIVER.implicitly_wait(1)
except NoSuchElementException:
print '您选择的测试地址出错!!'
def teardown(self):
'''
关闭浏览器
'''
self.DRIVER.quit()
def geturl(self,url):
'''
打开指定的网址
@param url: 要打开的网址
'''
self.DRIVER.get(url)
def selectvalue(self,findby,select,selectvalue):
'''
通过定制定位方法和要选择项的文本,选择指定的项目
@param findby:定位方法,如:byid,byname,byclassname等
@param select: 要执行选择操作的下拉框句柄
@param selectvalue: 下拉框中要选择项的文本
'''
if(findby == 'byid'):
select = Select(self.findelementbyid(select))
elif(findby =='byname'):
select = Select(self.findelementbyname(select))
elif(findby =='byclassname'):
select = Select(self.findelementbyclassname(select))
select.select_by_visible_text(selectvalue)
def inputvalue(self,findby,elmethod,value):
'''
通过定制定位方法,在输入框中输入值
@param findby: 定位方法,如:byid,byname,byclassname,byxpath等
@param elmethod: 要定位元素的属性值 ,如:id,name,class name,xpath等
@param value: 要给文本框输入的值
'''
if(findby == 'byid'):
self.findelementbyid(elmethod).send_keys(value)
elif(findby == 'byname'):
self.findelementbyname(elmethod).send_keys(value)
elif(findby =='byclassname'):
self.findelementbyclassname(elmethod).send_keys(value)
elif(findby == 'byxpath'):
self.findelementbyxpath(elmethod).send_keys(value)
……
上面的代码都有很详细的注释,在此就不一一讲述了。
6.6.2 QT_Operations类的内容
QT_Operations类是业务相关的公用功能块的封装,以便增加函数的公用性,减少代码量。例如,登录,在很多测试用例的第一步都需要先登录再操作的。所以你可以抽像出测试用例中的功能模块,封装后放到这个类中。
QT_Operations类的部分代码展示:
#-*- coding: UTF-8 -*-
'''
Created on 2014-12-15
@author:songxianfeng
@copyright: V1.0
'''
import time
import win32api
import win32con
from WebDriverHelp import WebDriverHelp
class QT_Operations(object):
'''
众筹前台相关操作
'''
def login(self,userName,passwd):
'''
从首页直接登录
@param userName: 用户名
@param passwd:密码
@param type1:指示登录方式,1为从主页登录,2,从登录页登录
'''
WebDriverHelp().clickitem("byclassname", "Js-showLogin")
time.sleep(3)
WebDriverHelp().clearvalue('byname','username')
WebDriverHelp().inputvalue('byname','username',userName)
WebDriverHelp().clearvalue('byname','user_pwd')
WebDriverHelp().inputvalue('byname','user_pwd',passwd)
time.sleep(1)
WebDriverHelp().clickitem("byclassname", "a-btn")
time.sleep(5)
def logout(self):
'''
退出登录
'''
WebDriverHelp().geturl("http://www.zhongchou.cn/usernew-loginout")
……
展示部分只包含了登录和退出功能,其他的功能可以根据测试用例的需要进行添加。
6.6.3 DataOperations类的内容
DataOperations类是对测试数据进行读取的操作,我们是用xml来存放测试数据的,所以测试用例执行的时候,需要先将测试数据读取出来,传递给相应的函数来对测试用例进行执行。然后根据执行的结果,判断测试用例是否执行成功。
DataOperations的内容:
#-*- coding: UTF-8 -*-
'''
Created on 2014-12-15
@author:songxianfeng
@copyright: V1.0
'''
import MySQLdb
from xml.dom import minidom
global DOC,CONN
class DataOperations(object):
'''
数据读取相关操作
'''
def __init__(self,filename):
'''
初始化xml文档
'''
global DOC,CONN
DOC = minidom.parse("../testData/"+filename)
def readxml(self,ftagname,num,stagname):
'''
从指定的文件中中读取指定节点的值
@param ftagname:起始节点的名称,如:project
@param num:取与起始节点相同的第num个节点
@param stagname: 起始节点下的二级节点
@return: 返回二级节点的值
'''
root = DOC.documentElement
message=root.getElementsByTagName_r(ftagname)[num]
return message.getElementsByTagName_r(stagname)[0].childNodes[0].nodeValue
def readxml_attribute(self,ftagname,num,stagname,attributeName):
'''
从all_case.xml文件中读取节点的属性值
@param ftagname:起始节点的名称,如:project
@param num:取与起始节点相同的第num个节点
@param stagname: 起始节点下的二级节点
@param attributeName: 二级节点的属性名
@return:返回二级节点指定的属性值
'''
root = DOC.documentElement
message=root.getElementsByTagName_r(ftagname)[num]
return message.getElementsByTagName_r(stagname)[0].getAttribute(attributeName)
python对xml的读取操作,如果你不太明白,可以去自行学习相关的内容,要教程不讲解相关的操作。
6.6.3 具体的测试用例
上面的公用方法类创建以后,我们就可以着手编写具体的测试用例了。在TestCases文件夹下创建测试用例TestCase_QT_Login.py,然后编写下面的测试步骤:
(1) 打开众筹网。
(2) 点击登录按钮,输入用户名和密码。
(3) 验证是否登录成功,用户昵称是不是刚刚登录的账号。
(4) 退出登录,关闭浏览器。
测试用例代码如下:
-*- coding: UTF-8 -*-
'''
Created on 2014-12-15
@author:songxianfeng
@version: v1.0
'''
import unittest
import time
#导入需要的公共函数类
from CommonFunction.WebDriverHelp import WebDriverHelp
from CommonFunction.DataOperations import DataOperations
from CommonFunction.QT_Operations import QT_Operations
class testcases_login(unittest.TestCase):
'''
登录检测
'''
def setUp(self):
WebDriverHelp("open","firefox","local").setup("zhengshi")#打开浏览器,并打开众筹网
def testlogin(self):
#登录检测
dataoper=DataOperations("TestCase_QT_Login.xml") #读取测试数据
#登录
QT_Operations().login(dataoper.readxml('login', 0, 'username'),
dataoper.readxml('login', 0, 'password'))
self.assertEqual(WebDriverHelp().gettext('byxpath',dataoper.readxml('login', 0, 'checkpoint1')),dataoper.readxml('login', 0, 'value1')) #判断登录是否成功
#退出
QT_Operations().logout()
time.sleep(5)
self.assertEqual(WebDriverHelp().gettext('byclassname',dataoper.readxml('login', 0, 'checkpoint2')),dataoper.readxml('login', 0, 'value2')) #判断退出是否成功
def tearDown(self):
WebDriverHelp().teardown()#关闭浏览器
if __name__ == "__main__":
unittest.main()
经过我们上面的封装,现在具体的测试用例只是传参数,调用具体的函数,验证执行结果。是不是非常简单?有点儿像我们小时候玩积木,用一块块现成的积木堆积出魔幻的城堡。
6.6.4 测试用例的具体数据
由于我们把测试用例和测试数据完全分离开了,所以我们用和测试用例同名的文件名命名对应的测试数据文件。登录测试用例的数据文件是TestCase_QT_Login.xml,具体内容如下: