整理不易,希望对各位学习软件测试能带来帮助

第四章 自动化测试模型

一个自动化测试框架就是一个集成体系,在这一体系中包含测试功能的函数库、测试数据源、测试对象识别标准,以及种可重用的模块。自动化测试框架在发展的过程中经历了几个阶段,模块驱动测试、数据驱动测试、对象驱动测试。本章就带领读者了解这几种测试模型。

第一节、自动化测试模型介绍

自动化测试模型是自动化测试架构的基础,自动化测试的发展也经历的不同的阶段,不断有新的模型(概念)被提出,了解和使用这些自动化模型将帮助我们构建一个灵活可维护性的自动化架构。

4.1.1 线性测试

通过录制或编写脚本,一个脚本完成一个场景(一组完整功能操作),通过对脚本的回放来进行自动化测试。这是早期进行自动化测试的一种形式;我们在上一章中练习使用 webdriver API 所编写的脚本也是这种形式。

测试脚本一

from selenium import webdriver
import time
driver = webdriver.Firefox()
driver.get("http://wwww.xxx.com")
driver.find_element_by_id("tbUserName").send_keys("username")
driver.find_element_by_id("tbPassword").send_keys("123456")
driver.find_element_by_id("btnLogin").click()
#执行具体用例操作
......
driver.quit ()

测试脚本二

from selenium import webdriver
import time
driver = webdriver.Firefox()
driver.get("http://wwww.xxx.com")
driver.find_element_by_id("tbUserName").send_keys("username")
driver.find_element_by_id("tbPassword").send_keys("123456")
driver.find_element_by_id("btnLogin").click()
#执行具体用例操作
......
driver.quit ()

通过上面的两个脚本,我们发现它优势就是每一个脚本都是独立的,任何一个脚本文件拿出来就能单
独运行;当然,缺点也很明显,用例的开发与维护成本很高:

一个用例对应一个脚本,假如登陆发生变化,用户名的属性发生改变,不得不需要对每一个脚本进行
修改,测试用例形成一种规模,我们可能将大量的工作用于脚本的维护,从而失去自动化的意义。

这种模式下数据和脚本是混在一起的,如果数据发生变也需要对脚本进行修改。这种模式下脚本的没
有可重复使用的概念。

4.1.2 模块化与类库

我们会清晰的发现在上面的脚本中,其实有不少内容是重复的;于是我们就考虑能不能把重复的部分
写成一个公共的模块,需要的时候进行调用,这样就大大提高了我们编写脚本的效率。
login.py

#登录模块
def login():
driver.find_element_by_id("tbUserName").send_keys("username")
driver.find_element_by_id("tbPassword").send_keys("456123")
driver.find_element_by_id("btnLogin").click()

quit.py

#退出模块
def quit_():
..............

测试用例:

#coding=utf-8
from selenium import webdriver
import login,quit_ #调用登录、退出模块
driver = webdriver.Firefox()
driver.get("http://wwww.xxx.com")
#调用登录模块
login.login()
#其它个性化操作
......
#调用退出模块
quit.quit_()

注意,上面用例非完整代码。

通过阅读上面的代码发现,我们可以把脚本中相同的部分代码独立出来,形成模块或库;这样做有两
方面的优点:

一方面提高了开发效率,不用重复的编写相同的脚本;假如,我已经写好一个登录模块,我后续需要
做的就是在需要的地方调用,不同重复造轮子。

另一方面方便了代码的维护,假如登录模块发生了变化,我只用修改 login.py 文件中登录模块的代
码即可,那么所有调用登录模块的脚本不用做任何修改。

4.1.3 数据驱动

数据驱动应该是自动化的一个进步;从它的本意来讲,数据的改变(更新)驱动自动化的执行,从而
引起测试结果的改变。这显然是一个非常高级的概念和想法。其实,我们可直白的理解成参数化,输入数据的不同从而引起输出结果的变化。

#coding=utf-8
from selenium import webdriver
import time
values=['selenium','webdriver',u'虫师']
# 执行循环
for serch in values:
driver = webdriver.Firefox()
driver.get("http://www.xxxx.com")
driver.find_element_by_id("kw").send_keys(serch)
time.sleep(3)
.....

不管我们读取的是数组,还是字典、函数,又或者是 csv、txt 文件。我们实现了数据与脚本的分离,
换句话说,我们实现了参数化。我们传一千条数据,通过脚本的执行,可以返回一千条结果出来。

同样的脚本执行不同的数据从而得到了不同的结果,是不是增强的脚本的复用性呢!?

其实,模块化与参数化这对开发来说是完全没有什么技术含量的;对于当初 QTP 自动化工具来说地确是一个卖点,因为它面对的大多是不懂开发的测试,当然,随着时代的发展,懂开发的测试人员越来越多。

4.1.4 关键字驱动

理解了数据驱动,无非是把“数据”换成“关键字”,通过关键字的改变引起测试结果的改变。
关键字驱动用编程方式就不太容易表现了。QTP、robot framework 等都是以关键字驱动为主的自动
化工具,因为这类工具主打的易用性,“填表格”式的关键字驱动帮我们封装了很多底层的东西,我们只
要考虑三个问题就可以了:我要做什么? 对谁做?怎么做?。
我们可以把 selenium IDE 看做是一种关键字驱动的自动化工具。

自动化脚本转化成表格是这样的:

Selenium IDE 脚本分:命令(command)、对象(target)、值(value)

通过这样的格式去描述不同的对象,从而引起最终结果的改变。也就是说一切以对象为出发点。当然,
这样的脚本,显然对于不懂代码的同学非常直观!我要做什么(命令)?对谁做(对象)?怎么做(值)?

更高级的关键字驱动,可以自己定义 keyword 然后“注册”到框架;从而实现更强大的功能和扩展性。

小结:

这里简单介绍了自动化测试的几种不同的模型,虽然简单阐述了他们的优缺点,但他们并非后者淘汰
前者的关系,在实施自动化更多的是以需求为出发点,混合的来使用以上模型去解决问题;使我们的脚本更易于开发与维护。

第二节、登录模块化

通过上一节对测试模型的学习可以发现,在我们的目前的脚本中有很多代码是可以模块化的,比如登录模块。我们的每一个用例的执行都需要登录脚本,那可我们是否可以将登录脚本独立到单独的文件调用。

下面以快播私有云的登录退出测试用例为例:
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()

注明:本用例暂不关注 unittest 内容,第六章会详细的讲解 unittest 单元测试框架。
从业务流程及用例分析,每一个自动化测试用例的执行过程为:先执行登录操作,然后执行具体的操
作(如文件/文件夹的创建、删除、移动、重命名等操作),最后执行退出操作。如上面的测试用例,登录与退出操作是相对固定的,那么我们可以把登录与退出操作进行模块化,然后调用,一方面不用写重复代码,另一方面可以使测试用例更关注具体的用例代码。

在与 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()
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 基础知识补充
进行到这里,我们有必要补充一下 python 语言中函数、类、方法的使用,这将有助于我们自动化测
试脚本的开发。下面打开 python IDLE:
函数的基本使用:
#例1

>>> def add(a,b):
c=a+b
print c>>> add(1,3)
4
#例2
>>> def add2(a=1,b=3):
c=a+b
return c
>>> d=add2()
>>> print d
4

通过 def 关键字可创建函数,在例1中我们创建了 add()函数,默认接收两个参数化 a、b,把 a、b 相
加结果给 c,并将结果函数内打印。

例2中创建了 add2()函数,这一次对 a、b 设置了默认值,同样对 a、b 做加法,并将结果用 return 返回;d 在接收 add2()时用的是默认值,将后将 d 接收的结果打印。

类与方法的基本使用:

>>> class Counter:
def add(self,a,b):
c=a+b
print c
def subtract(self,a,b):
c=a-b
print c
>>> d=Counter()
>>> d.add(5,3)
8
>>> d.subtract(5,3)
2

通过 class 关键字我们创建了一个 Counter 类,定义了 add()和 subtract()两个方法分别来完成加法
和减法运算,并将计算结果打印。

通过上面的例子我们明显的发现类的方法与函数有一个明显的区别,在类的方法中必须有个额外的第一个参数(self),但在调用类的方法时却不必为这个参数赋值。self 参数所指的是对象本身,所以习惯
性地命名为 self。

为何 Python 给 self 赋值而你不必给 self 赋值?
创建了一个类 MyClass,实例化 MyClass 得到了 MyObject 这个对象,然后调用这个对象的方法
MyObject.method(a,b),在这个过程中,Python会自动转为Myclass.method(MyObject,a,b),这就是Python
的 self 的原理。即使你的类的方法不需要任何参数,但还是得给这个方法定义一个 self 参数,虽然我们
在实例化调用的时候不用理会这个参数。
下面回到用例本身来讨论如何模块化和调用的,在 login.py 文件中:

def login(self):
driver = self.driver

这里用到的是方法,(driver = self.driver)driver 为对象身的 driver,这一句很重要,否则我们
无法在 ligin()方法中使用 driver 操作浏览器。

在 webdriver.py 文件中:

#导入登录文件
import login
......
#调用登录模块
login.login(self)
......

首先导入 login 文件,然后对文件中的 login()方法进行调用。
小作业:
下面笔者自已动手把退出的相关操作也进行模块化吧!(提示,可以单独创建一个退出文件写退出函数,也可以在 login.py 中写退出函数;当然也可以写一个登陆类,分别写登陆和退出方法。)

第三节、数据驱动(参数化)

在测试模型一节的数据驱动中我们已经介绍了如何通过 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)。这样通过循环调用,直到文件的中的所有内容全被读取。

登录参数化(读取 txt 文件)

现在按照上面的思路,对自动化脚本中用户、名密码进行参数化,通过 python 文档我们发现 python读取文件的方式有:整个文件读取、逐行读取、固定字节读取。并没有找到一次读取两条数据的好方法。
创建两个文件,分别存放用户名密码,如图:

打开之前编写的 login.py 文件,做如下修改:

#coding=utf-8
from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException
博客园---虫师
http://fnng.cnblogs.com 105
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 文件:

>>> ================================ RESTART ================================
>>>
success reader username and password!!
testing 123456

说明我们对函数的参数调用是成功的,将 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]

运行结果:

>>> ========================= RESTART ==============================>>>
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 ,你一定可以找到更完美优雅的方法;解决问题的方法是多样的,使用最贴合需求的方法,简单解决问题。这一节写的比较多,对构建自动化框架来说,参数化是非常重要的一个知识点。

脚本的模块化与参数化是我们在自动化脚本开发中用到最多的两个技巧,希望读者能认真体会,灵活
的运行到具体的项目中。

未完待续

软件测试打卡交流学习社区

【软件测试】自动化测试战零基础教程——Python自动化从入门到实战(五)相关推荐

  1. 【软件测试】自动化测试战零基础教程——Python自动化从入门到实战(六)

    整理不易,希望对各位学习软件测试能带来帮助 软件测试知识持续更新 第五章 自动化测试用例设计 第一节.手工测试用例与自动化测试用例 手工测试用例与自动化测试用例对比: 用例选型注意事项: 第二节.测试 ...

  2. 【软件测试】自动化测试战零基础教程——Python自动化从入门到实战(一)

    第一章:自动化测试基础 第一节 软件测试分类 关于软件测试领域名词颇多,发现有许多测试新手混淆概念,从不同的角度可以将软件测试有不同的分类的方法:所以,这里汇总常见软件测试的相关名词,对软件测试领域有 ...

  3. 【软件测试】自动化测试战零基础教程——Python自动化从入门到实战(完结)

    整理不易,希望对各位学习软件测试能带来帮助 软件测试知识持续更新 第十章 行为驱动开发 BDD 框架 lettuce 入门 第一节.安装与例子 安装 例子(阶乘) 第二节.lettuce 解析 第三节 ...

  4. 【软件测试】自动化测试战零基础教程——Python自动化从入门到实战(八)

    整理不易,希望对各位学习软件测试能带来帮助 软件测试知识持续更新 第七章 引入测试报告与结构优化 第一节.生成 HTMLTestRunner 测试报告 第二节.测试套件 7.2.1.测试套件实例 7. ...

  5. 编程没基础学python多长时间--零基础学Python,从入门到精通需要多长时间

    求一份小甲鱼的<零基础入门学习Python>视频教程 评论 本系列教程面向础的同学,是一个深入浅通俗易懂的Python3视频教程. 适群 完全零基础入门,不需要任何前置知识. 教程概述 前 ...

  6. python从入门到精通需要多久--零基础学Python,从入门到精通需要多长时间

    求一份小甲鱼的<零基础入门学习Python>视频教程 评论 本系列教程面向础的同学,是一个深入浅通俗易懂的Python3视频教程. 适群 完全零基础入门,不需要任何前置知识. 教程概述 前 ...

  7. 零基础自学Python编程从入门到精通基础教程《从零开始学Python》

    推荐理由 本书面向零基础读者,巧用类比式描述,技术知识点轻松掌握: 基于案例进行讲解,读者可轻松理解编程思维,并在配套代码中参透Python编程的技巧. 本书囊括5项常见任务,助力快速掌握Python ...

  8. CrazyWing:Python自动化运维开发实战 六、流程控制

    Python 条件语句 Python条件语句是通过一条或多条语句的执行结果(True或者False)来决定执行的代码块. Python程序语言指定任何非0和非空(null)值为true,0 或者 nu ...

  9. python入门到精通需要学多久-廖雪峰python教程要学多久-零基础学Python需要多久...

    零基础学python大约需要多久 看不同的人,不同的学习能和基础. 像我通java,vc ,javascript,groovy,vb,c 接触过c#,delphi,asp,E语言, 用过dreamwa ...

最新文章

  1. SkipList 跳表
  2. 用python绘制柱状图标题-Python笔记:用Python绘制炫酷的柱形图
  3. sudo运行程序遇到的问题
  4. 供应链金融与区块链技术-可以研读
  5. matlab画倾斜的椭球,在MATLAB中绘制椭圆和椭球
  6. 【机器学习基础】一文归纳AI调参炼丹之法
  7. 赠书福利 | 首本理论和实战结合的深度学习书籍
  8. 初步认识Volatile-缓存一致性协议
  9. html登录注册的正则,怎么用html5编写用户注册验证程序
  10. .NET6之MiniAPI(二十):实体验证FluentValidation
  11. Mac安装redis与后台启动
  12. Netty工作笔记0038---Netty模型--通俗版
  13. 微软警告员工不要参与愚人节恶作剧
  14. python xlwt操作excel
  15. 从互联网大厂跳槽到国企后,我发现没有一劳永逸的工作。。。
  16. input file选择图片后预览 单图和多图
  17. 环境工程原理知识重点归纳
  18. BI 是如何数据分析的?
  19. python编程用什么软件?
  20. 大数据与云计算学习计划 (一) 云计算系统管理 3 Linux系统命令行基础 (概念与实操)

热门文章

  1. qsql 关联_QSqlTableModel qt 数据库读写 使用方法(转)
  2. php 审核功能,随缘网络PHP留言板(带审核功能)
  3. 取消win7自动登录
  4. Leave me alone ! 程序员怎样度过他的一天?
  5. AIOPS 学习之路
  6. 《用户体验要素—以用户为中心的Web开发》笔记
  7. 8.尚硅谷电商推荐系统预览
  8. MOXA Nport在二所ATC的使用方式(川大是UDP方式)
  9. 深度学习pipeline和baseline是什么意思?
  10. MFC中使用SKIN++