登陆模块化
webcloud.py
#coding=utf-8 from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from selenium.webdriver.support.ui import Select from selenium.common.exceptions import NoSuchElementException import unittest, time class Login(unittest.TestCase): def setUp(self): self.driver = webdriver.Firefox() self.driver.implicitly_wait(30) self.base_url = "http://passport.kuaibo.com" self.verificationErrors = [] self.accept_next_alert = True #私有云登录用例 def test_login(self): driver = self.driver driver.get(self.base_url + "/login/?referrer=http%3A%2F%2Fwebcloud.kuaibo.com%2F") driver.maximize_window() #登陆 driver.find_element_by_id("user_name").clear() driver.find_element_by_id("user_name").send_keys("username") driver.find_element_by_id("user_pwd").clear() driver.find_element_by_id("user_pwd").send_keys("123456") driver.find_element_by_id("dl_an_submit").click() time.sleep(3) #新功能引导 driver.find_element_by_class_name("guide-ok-btn").click() time.sleep(3) #退出 driver.find_element_by_class_name("Usertool").click() time.sleep(2) driver.find_element_by_link_text("退出").click() time.sleep(2) def tearDown(self): self.driver.quit() self.assertEqual([], self.verificationErrors) if __name__ == "__main__": unittest.main()
从业务流程及用例分析,每一个自动化测试用例的执行过程为:先执行登录操作,然后执行具体的操作(如文件/文件夹的创建、删除、移动、重命名等操作),最后执行退出操作。如上面的测试用例,登录与退出操作是相对固定的,那么我们可以把登录与退出操作进行模块化,然后调用,一方面不用写重复代码,另一方面可以使测试用例更关注具体的用例代码。
在与 webcloud.py 相同的目录下创建 login.py 文件,脚本内容如下
#coding=utf-8 from selenium import webdriver from selenium.common.exceptions import NoSuchElementException import unittest, time #登陆模块(函数) def login(self): driver = self.driver driver.maximize_window() driver.find_element_by_id("user_name").clear() driver.find_element_by_id("user_name").send_keys("username") driver.find_element_by_id("user_pwd").clear()
#coding=utf-8 from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from selenium.webdriver.support.ui import Select from selenium.common.exceptions import NoSuchElementException import unittest, time import login #导入登录文件 class Login(unittest.TestCase): def setUp(self): self.driver = webdriver.Firefox() self.driver.implicitly_wait(30) self.base_url = "http://passport.kuaibo.com" self.verificationErrors = [] self.accept_next_alert = True #私有云登录用例
driver.find_element_by_id("user_pwd").send_keys("123456") driver.find_element_by_id("dl_an_submit").click() time.sleep(3)
webcloud.py
#coding=utf-8 from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from selenium.webdriver.support.ui import Select from selenium.common.exceptions import NoSuchElementException import unittest, time import login #导入登录文件 class Login(unittest.TestCase): def setUp(self): self.driver = webdriver.Firefox() self.driver.implicitly_wait(30) self.base_url = "http://passport.kuaibo.com" self.verificationErrors = [] self.accept_next_alert = True
def test_login(self): driver = self.driver driver.get(self.base_url + "/login/?referrer=http%3A%2F%2Fwebcloud.kuaibo.com%2F") #调用登录模块 login.login(self) #新功能引导 driver.find_element_by_class_name("guide-ok-btn").click() time.sleep(3) #退出 driver.find_element_by_class_name("Usertool").click() time.sleep(2) driver.find_element_by_link_text("退出").click() time.sleep(2) def tearDown(self): self.driver.quit() self.assertEqual([], self.verificationErrors) if __name__ == "__main__": unittest.main()
数据驱动
在测试模型一节的数据驱动中我们已经介绍了如何通过 python 定于的数组对百度输入数据进行参数化设置,将其它循环的读取 vlalues 数组中每一个数据。这里我们将通过读取 txt 文件中的数据来实现参数化。
创建 data.txt 文件,向文件内写放三行数据
d:\abc\data.txt
baidu_read_data.py
#coding=utf-8 from selenium import webdriver import os,time source = open("D:\\abc\\data.txt", "r") values = source.readlines() source.close() # 执行循环 for serch in values: browser = webdriver.Firefox() browser.get("http://www.baidu.com") browser.find_element_by_id("kw").send_keys(serch) browser.find_element_by_id("su").click() browser.quit()
open 方法以只读方式(r)打开本地的 data.txt 文件,readlines 方法是逐行的读取文件内容
通过 for 循环,serch 可以每次获取到文件中的一行数据,在定位到百度的输入框后,将数据传入send_keys(serch)。这样通过循环调用,直到文件的中的所有内容全被读取
登录参数化
现在按照上面的思路,对自动化脚本中用户、名密码进行参数化,通过 pythonn 文档我们发现 python读取文件的方式有:整个文件读取、逐行读取、固定字节读取。并没有找到一次读取两条数据的好方法。创建两个文件,分别存放用户名密码
打开之前编写的 login.py 文件,做如下修改:
#coding=utf-8 from selenium import webdriver from selenium.common.exceptions import NoSuchElementException import unittest, time, os source = open("D:\\selenium_python\\data\username.txt", "r") #用户名文件 un = source.read() #读取用户名 source.close() source2 = open("D:\\selenium_python\\data\password.txt", "r") #密码文件 pw = source2.read() #读取密码 source2.close() def login(self): driver = self.driver driver.maximize_window() driver.find_element_by_id("user_name").clear() driver.find_element_by_id("user_name").send_keys(un) driver.find_element_by_id("user_pwd").clear() driver.find_element_by_id("user_pwd").send_keys(pw) driver.find_element_by_id("dl_an_submit").click() time.sleep(3)
分别打开两个 txt 文件,通过 un 和 pw 来接收用户名和密码信息,将接收的数据通过 send_key(xx) 转入到执行程序中。
运行我们前面创建的 webcloud.py 文件,程序可以正常的执行。虽然这样做比较丑,但是确实达到了数据与脚本分离的目的。
缺点
虽然目的达到了这,但这样的实现有很多问题:
1、用户名密码分别在不同的文件里,修改用户名和密码比较麻烦。
2、username.txt 和 password.txt 文件中只能保存一个用户密码,无能很好的循环读取。
登录参数化
函数是我们前面刚介绍的 python 知识,函数可以预先给参数化赋值,借助这个特性,我们可以通过调用函数的方式对用户名密码进行参数化
userinfo.py
def fun(un='testing',pw=123456): print "success reader username and password!!" return un,pw
我们为两个参数 un 和 pw 赋了初值,赋值内容如果是字符串需要加引号,如果是数字可以不需要 引号。再次打开 login.py 文件,做如下修改:
#coding=utf-8 from selenium import webdriver from selenium.common.exceptions import NoSuchElementException import unittest, time import userinfo #导入函数 #通过两个变量,来接收调用函数获得用户名&密码 us,pw = userinfo.fun() #打印两个变量 print us,pw def login(self): driver = self.driver driver.maximize_window() driver.find_element_by_id("user_name").clear() driver.find_element_by_id("user_name").send_keys(un) driver.find_element_by_id("user_pwd").clear() driver.find_element_by_id("user_pwd").send_keys(pw) driver.find_element_by_id("dl_an_submit").click() time.sleep(3)
单独运行 login.py 文件
说明我们对函数的参数调用是成功的,将 un、pw 两个变量的值传入用户名密码的 send_keys()方法中 即可。
登录参数化(读取字典)
既然我们是固定的读取用户名和密码两个值,那么可以借助 python 字典的方式来完成这个需求。字 典由大括号内的多键值对组成;下面继续在 python IDLE 交互模式下演示字典的创建与使用。
>>> data = {'abc':'123','def':'456'} >>> print data {'abc': '123', 'def': '456'} >>> data.keys() ['abc', 'def'] >>> data.values() ['123', '456'] >>> data.items() [('abc', '123'), ('def', '456')]
创建字典用大括号,数据由 key/value 键值对组成,keys()方法返回字典中的键列表。values()返回 字典中的值列表,items()返回(key,value)元组。
下面创建一个存放字典的函数 文件 userinfo.py :
def zidian(): data={'username':'123456','testing360':'123123'} print "success reader username and password!!" return data
字典的可以方便的存放 k,v 键值对,一个键对应一个值;注意,如果密码中有非数字,需要加引号
需要说明的是我们的需求并不适合循环的读取用户名密码,不过我们可以写一小程序单独验证这种循 环读取的方式:
loop_reader.py
#coding=utf-8 import userinfo #导入函数 #获取字典数据 info = userinfo.zidian() #通过 items()循环读取元组(键/值对) for us,pw in info.items(): print us print pw
运行结果如下:
>>> ================================ RESTART ================================ >>> success reader username and password!! username 123456 testing360 123456
在实际使用中,可以将 un、pw 两个变量循环获得的值传入相应的 send_keys()方法中。 目前方式解决了两个问题:一次传入两个值,以及循环读取。
表单参数化(csv)
假如我有自动化脚本中要参数化一张表单,表单需要填写用户名、邮箱,年龄,性别等信息,使用上 面的方法就很难来解决这个问题,下面通过读取 csv 文件的方法来解决这个问题。
创建 userinfo.csv 文件,
通过 WPS 或 excel 创建表格,文件另存为选择 CSV 格式,下面修改 loop_reader.py 文件进行循环读 取
#coding=utf-8 import csv #导入 csv 包 #读取本地 CSV 文件 my_file='D:\\selenium_python\\data\\userinfo.csv' data=csv.reader(file(my_file,'rb')) #循环输出每一行信息 for user in data: print user[0] print user[1] print user[2] print user[3]
运行结果:
>>> testing 123456@126.com 23 男 testing2 123123@qq.com 18 女 testing3 456123@gmail.com 29 女
csv.reader()用于读取 CSV 文件,user[0] 表示表格中第一列的数据(用户名),user[0]表示表格中第二 列的数据(邮箱),后面类推。
通过 CSV 读取文件比较灵活,可以循环读取每一条数据,从而又不局限每次所读取数据的个数。
我们这里举例了多种方式来进行参数化,虽然最终看来 CSV 的方式最灵活,这里更多的是想告诉读 者解决问题的方法是多样的,我们可以用 python 的各种技巧来选择最简单的方法来解决问题。从这个过程 中我们也学到了不少 python 编程技术。
我这里可以说是用 python 最基础的知识点解决了问题,这里只算提供一个思路,温习一下 python , 你一定可以找到更完美优雅的方法;解决问题的方法是多样的,使用最贴合需求的方法,简单解决问题。 这一节写的比较多,对构建自动化框架来说,参数化是非常重要的一个知识点。
脚本的模块化与参数化是我们在自动化脚本开发中用到最多的两个技巧,希望读者能认真体会,灵活 的运行到具体的项目中。