python基于Selenium的web自动化框架


Posted in Python onJuly 14, 2019

1 什么是selenium

Selenium 是一个基于浏览器的自动化工具,它提供了一种跨平台、跨浏览器的端到端的web自动化解决方案。Selenium主要包括三部分:Selenium IDE、Selenium WebDriver 和Selenium Grid:

  • Selenium IDE:Firefox的一个扩展,它可以进行录制回放,并可以把录制的操作以多种语言(例如java,python等)的形式导出成测试用例。
  • Selenium WebDriver:提供Web自动化所需的API,主要用作浏览器控制、页面元素选择和调试。不同的浏览器需要不同的WebDriver。
  • Selenium Grid:提供了在不同机器的不同浏览器上运行selenium测试的能力

本文中主要使用python结合Selenium WebDriver库进行自动化测试框架的搭建。

2 自动化测试框架

一个典型的自动化测试框架一般包括用例管理模块、自动化执行控制器、报表生成模块和日志模块等,这些模块之间不是相互孤立的,而是相辅相成的。

python基于Selenium的web自动化框架

下面来介绍下每个模块的逻辑单元:

  • 用例管理模块

用例管理模块包括用例的添加、修改、删除等操作单元,这些单元也会涉及到用例书写的模式,测试数据的管理、可复用库等

  • 自动化执行控制器

控制器是自动化用例执行的组织模块,主要负责以什么方式去执行用例。比较典型的控制器有用户图形界面(GUI)和“commandline+文件”两种。

  • 报表生成模块

报表生成模块主要负责执行完用例以后生成报表,报表一般以HTML格式居多,信息主要包括用例的执行情况及相应的总结报告。另外还可以添加发送邮件功能。

  • 日志模块

日志模块主要用来记录用例的执行情况,以便于更高效的调查用例失败信息及追踪用例执行情况。

3 自动化框架的设计与实现

3.1 需求分析

测试对象是一个典型的后台系统的Web展现平台,基于此平台设计的自动化框架要包含测试用例管理、测试执行控制、测试报表及测试日志的生成,整体测试框架要轻量易用。

3.2 概要设计

概要设计包括了四个大的模块:公共库模块(可复用函数、日志管理、报表管理以及发送邮件管理)、用例仓库(具体用例的管理)、页面管理(单独对Web页面进行抽象,封装页面元素和操作方法)以及执行模块。

概要设计类图:

python基于Selenium的web自动化框架

3.3 详细设计与实现

3.3.1 页面管理

测试Web对象是一个典型的单页面应用,因此采用页面模式(page pattern)来进行组织:

python基于Selenium的web自动化框架

页面模式是页面与测试用例之间的桥梁,它将每个页面抽象成一个单独的页面类,为测试用例提供页面元素的定位和操作。

页面模式的类图如下:

python基于Selenium的web自动化框架

BasePage作为基类只包含一个driver成员变量,它用来标记Selenium中的WebDriver,以便在BasePage的派生类中定位页面元素。LoginPage和PageN等作为派生类,可以提供相应页面元素的定位和操作方法。比如测试对象的登录页面:

python基于Selenium的web自动化框架

从页面可以看出,需要操作的页面元素分别为:Username,Password,remember my username checkbox和Sign in按钮,它们对应的操作为输入用户名和密码,点选checkbox和点击Sign In按钮,具体代码级别的实现如下:

页面基类BasePage.py:

class BasePage(object): 
  """description of class""" 
 
  #webdriver instance 
  def __init__(self, driver): 
    self.driver = driver

LoginPage页面继承自BasePage,并进行Login Page的元素定位及操作实现。代码中定位了username和password,并且添加了设置用户名和密码的操作。

from BasePage import BasePage 
from selenium.webdriver.common.by import By 
from selenium.webdriver.common.keys import Keys 
 
class LoginPage(BasePage): 
  """description of class""" 
   #page element identifier 
  usename = (By.ID,'username') 
  password = (By.ID, 'password') 
  dialogTitle = (By.XPATH,"//h3[@class=\"modal-title ng-binding\"]") 
  cancelButton = (By.XPATH,'//button[@class=\"btn btn-warning ng-binding\"][@ng-click=\"cancel()\"]') 
  okButton = (By.XPATH,'//button[@class=\"btn btn-primary ng-binding\"][@ng-click=\"ok()\"]') 
 
  #Get username textbox and input username 
  def set_username(self,username): 
    name = self.driver.find_element(*LoginPage.usename) 
    name.send_keys(username) 
   
  #Get password textbox and input password, then hit return 
  def set_password(self, password): 
    pwd = self.driver.find_element(*LoginPage.password) 
    pwd.send_keys(password + Keys.RETURN) 
 
  #Get pop up dialog title 
  def get_DiaglogTitle(self): 
    digTitle = self.driver.find_element(*LoginPage.dialogTitle) 
    return digTitle.text 
 
  #Get "cancel" button and then click 
  def click_cancel(self): 
    cancelbtn = self.driver.find_element(*LoginPage.cancelButton) 
    cancelbtn.click() 
 
   #click Sign in 
  def click_SignIn(self): 
    okbtn = self.driver.find_element(*LoginPage.okButton) 
    okbtn.click()

采用页面模式来管理页面和测试用例有很多好处,主要体现在:

  • 简单并且清晰

每个页面都有单独的类来封装页面元素和操作,让页面操作更加具体化,而不是相对独立的。

比如未使用页面模式,测试用例的输入用户名和密码的代码:

#enter username and password 
driver.find_element_by_id("username").clear() 
driver.find_element_by_id("username").send_keys("sbxadmin") 
driver.find_element_by_id("password").clear() 
driver.find_element_by_id("password").send_keys("password"+Keys.RETURN)

使用页面模式之后,输入用户名和密码的代码:

#Step2: Open Login page 
login_page = BasePage.LoginPage(self.driver) 
#Step3: Enter username 
login_page.set_username("username") 
#Step4: Enter password 
login_page.set_password("password")

通过对比我们不难发现,未使用页面模式的代码组织比较混乱,步骤多,可读性非常差,不难想象,一个通篇都是find_element_by_id或者send_Keys的测试用例到底有多糟糕!而使用了页面模式之后,在哪个页面做什么操作都非常清晰,非常接近测试用例的步骤,易读性非常好。

  • 可复用性好

由于页面操作都被封装在了页面类中,所以页面方法和容易调用,可复用性非常好。而未使用页面模式的用例只能每次都实现一遍。

  • 可维护性好

由于测试目标页面的多变性,页面元素的定位经常需要改变,利用了页面模式后,只需要修改一遍其页面类中的定位就可以对所用用到该元素的测试用例生效;而在未使用该模式的情况下,必须修改每一个用到该元素的测试用例,非常容易遗漏,工作量也非常大。

综合以上页面模式的各种优点,我们在以后的web自动化中可以多使用该模式来组织页面。

3.3.2 公共库模块

公共库模块是为创建测试用例服务的,它主要包括常量、公共函数、日志管理、报表管理以及发送邮件管理等。

公共库模块涉及到的功能一般多而杂,在设计的时候只要遵循高内聚低耦合就可以了。比如常量、变量和一些公共函数可以放在同一个文件中Common.py:

from datetime import datetime 
 
def driverPath(): 
  return r'C:\Users\xua\Downloads\chromedriver_win32\chromedriver.exe' 
def baseUrl(): 
  return "https://xxx.xxx.xxx.xxx:9000" 
#change time to str 
def getCurrentTime(): 
  format = "%a %b %d %H:%M:%S %Y" 
  return datetime.now().strftime(format) 
# Get time diff 
def timeDiff(starttime,endtime): 
  format = "%a %b %d %H:%M:%S %Y" 
  return datetime.strptime(endtime,format) - datetime.strptime(starttime,format)

测试用例信息类用来标识测试用例,并且包括执行用例执行结果信息,主要包括以下字段:

class TestCaseInfo(object): 
  """description of class""" 
  def __init__(self, id="",name="",owner="",result="Failed",starttime="",endtime="",secondsDuration="",errorinfo=""): 
    self.id = id 
    self.name = name 
    self.owner = owner 
    self.result = result 
    self.starttime = starttime 
    self.endtime = endtime 
 self.secondsDuration = secondsDuration 
 self.errorinfo = errorinfo

测试用例信息需要在每个测试用例中实例化,以便对测试用例进行标记,并最终体现在测试报告中。

日志主要用来记录测试用例执行步骤及产生的错误信息,不同的信息有不同的日志级别,比如Information,Warning,Critical和Debug。由于每个测试用例产生的日志条目比较少,所以在测试框架中只利用了最高级别的日志打印,即Debug级别,该级别也会将其他所有的日志级别的信息同样打印出来。在具体的实现中引用了Python标准库中的logging类库,以便更方便的控制日志输出:

import logging 
import ResultFolder 
 
logger = logging.getLogger() 
logger.setLevel(logging.DEBUG) 
 
 
def CreateLoggerFile(filename): 
  try: 
    fulllogname = ResultFolder.GetRunDirectory()+"\\"+filename+".log" 
    fh = logging.FileHandler(fulllogname) 
    fh.setLevel(logging.DEBUG) 
    formatter = logging.Formatter('%(asctime)s [line:%(lineno)d] %(message)s') 
    fh.setFormatter(formatter) 
    logger.addHandler(fh) 
  except Exception as err: 
    logger.debug("Error when creating log file, error message: {}".format(str(err))) 
 
 
def Log(message): 
  logger.debug(message)

报表管理及发送邮件模块实现了报表(html格式)的生成及自动发送邮件的功能。报表和邮件依附于当前测试的执行,每次执行都会独立的触发报表生成和邮件发送。该模块主要运用了Python中的lxml、smtplib和email库。

3.3.3 用例仓库

用例仓库主要用来组织自动化测试用例。每条测试用例都被抽象成一个独立的类,并且均继承自unittest.TestCase类。 Python中的unittest库提供了丰富的测试框架支持,包括测试用例的setUp和tearDown方法,在实现用例的过程中可以重写。依托页面管理和公共库模块实现的页面方法和公共函数,每一个测试用例脚本的书写都会非常清晰简洁,一个简单的Floor Manager Lite的登录用例如下:

class Test_TC_Login(unittest.TestCase): 
  def setUp(self): 
    self.driver = webdriver.Chrome(cc.driverPath()) 
    self.base_url = cc.baseUrl() 
    self.testCaseInfo = TestCaseInfo(id=1,name="Test case name",owner='xua') 
    self.testResult = TestReport() 
    LogUtility.CreateLoggerFile("Test_TC_Login") 
  def test_A(self): 
    try: 
      self.testCaseInfo.starttime = cc.getCurrentTime() 
      #Step1: open base site 
      LogUtility.Log("Open Base site"+self.base_url) 
      self.driver.get(self.base_url) 
 
      #Step2: Open Login page 
      login_page = LoginPage(self.driver) 
 
      #Step3: Enter username & password 
      LogUtility.Log("Login web using username") 
      login_page.set_username("username") 
      login_page.set_password("password") 
 
      time.sleep(2) 
      #Checkpoint1: Check popup dialog title 
      LogUtility.Log("Check whether sign in dialog exists or not") 
      self.assertEqual(login_page.get_DiaglogTitle(),"Sign in") 
 
      #time.sleep(3) 
      #Step4: Cancel dialog 
      login_page.click_cancel() 
      self.testCaseInfo.result = "Pass" 
 
    except Exception as err: 
      self.testCaseInfo.errorinfo = str(err) 
      LogUtility.Log(("Got error: "+str(err))) 
    finally: 
      self.testCaseInfo.endtime = cc.getCurrentTime() 
      self.testCaseInfo.secondsDuration = cc.timeDiff(self.testCaseInfo.starttime,self.testCaseInfo.endtime) 
  def tearDown(self): 
    self.driver.close() 
    self.testResult.WriteHTML(self.testCaseInfo) 
 
if __name__ == '__main__': 
  unittest.main()

从这个测试用例中,我们可以看到

1.Setup中定义了执行测试用例前的一些实例化工作
2.tearDown对执行完测试做了清理和写日志文件工作
3.测试步骤、测试数据和测试检查点非常清晰,易修改(比如用户名密码)
4.日志级别仅有Debug,所以写日志仅需用同一Log方法

3.3.4 用例执行模块(控制器)

执行模块主要用来控制测试用例脚本的批量执行,形成一个测试集。用例的执行引用了Python标准库中的subprocess来执行nosetests的shell命令,从而执行给定测试用例集中的用例。测试用例集是一个简单的纯文本文件,实现过程中利用了.txt文件testcases.txt:

Test_Login_pass.py 
Test_Login_Fail.py 
#Test_MainPage_CheckSecurityTableInfo.py 
Test_MainPage_EditSecurityInfo.py

用例前没有“#“标记的测试用例脚本会被执行,而有”#“标记的则会被忽略,这样可以很方便的控制测试集的执行,当然也可以创建不同的文件来执行不同的测试集。

具体的调用代码如下:

def LoadAndRunTestCases(self): 
  try: 
    f = open(self.testcaselistfile) 
    testfiles = [test for test in f.readlines() if not test.startswith("#")] 
    f.close() 
    for item in testfiles: 
      subprocess.call("nosetests "+str(item).replace("\\n",""),shell = True) 
  except Exception as err: 
    LogUtility.logger.debug("Failed running test cases, error message: {}".format(str(err))) 
  finally: 
    EmailUtils.send_report()

3.4 执行结果

测试用例执行完毕后主要有两种输出:日志和测试报告。测试报告会html附件的形式通过邮件发出,例如:

python基于Selenium的web自动化框架

4 需要改进的模块

对于现有实现的测试框架,已经可以满足web对象的自动化需求,但还是有些可以改进提高的地方,比如:

针对部分测试用例是否可以尝试数据驱动添加屏幕截图功能封装selenium中By库中的函数,以便更高效的定位页面元素等结合业界优秀的自动化框架和实践持续改进

5 总结

基于selenium实现的web自动化框架不仅轻量级而且灵活,可以快速的开发自动化测试用例。结合本篇中的框架设计以及一些好的实践,希望对大家以后的web自动化框架的设计和实现有所帮助。

源代码:https://github.com/AlvinXuCH/WebAutomaiton

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
使用C语言扩展Python程序的简单入门指引
Apr 14 Python
python魔法方法-属性访问控制详解
Jul 25 Python
深入理解Python中的super()方法
Nov 20 Python
python3安装pip3(install pip3 for python 3.x)
Apr 03 Python
Python cv2 图像自适应灰度直方图均衡化处理方法
Dec 07 Python
Python字符串匹配之6种方法的使用详解
Apr 08 Python
python pycharm最新版本激活码(永久有效)附python安装教程
Sep 18 Python
Python datetime 格式化 明天,昨天实例
Mar 02 Python
python 弧度与角度互转实例
Apr 15 Python
python 实现控制鼠标键盘
Nov 27 Python
python 窃取摄像头照片的实现示例
Jan 08 Python
浅谈Python中的正则表达式
Jun 28 Python
Django项目使用CircleCI的方法示例
Jul 14 #Python
Python实现最常见加密方式详解
Jul 13 #Python
python Pandas库基础分析之时间序列的处理详解
Jul 13 #Python
简单了解python反射机制的一些知识
Jul 13 #Python
Python3内置模块之base64编解码方法详解
Jul 13 #Python
Python3enumrate和range对比及示例详解
Jul 13 #Python
基于Python的ModbusTCP客户端实现详解
Jul 13 #Python
You might like
php socket方式提交的post详解
2008/07/19 PHP
令PHP初学者头疼十四条问题大总结
2008/11/12 PHP
谈PHP生成静态页面分析 模板+缓存+写文件
2009/08/17 PHP
PHP使用Mysql事务实例解析
2014/09/08 PHP
PHP实现会员账号单唯一登录的方法分析
2019/03/07 PHP
使用laravel指定日志文件记录任意日志
2019/10/17 PHP
js实现的日期操作类DateTime函数代码
2010/03/16 Javascript
jquery下将选择的checkbox的id组成字符串的方法
2010/11/28 Javascript
jquery实现的让超出显示范围外的导航自动固定屏幕最顶上
2011/09/22 Javascript
获得所有表单值的JQuery实现代码[IE暂不支持]
2012/05/24 Javascript
基于JQuery的模拟苹果桌面Dock效果(稳定版)
2012/10/15 Javascript
JS 退出系统并跳转到登录界面的实现代码
2013/06/29 Javascript
在表单提交前进行验证的几种方式整理
2013/07/31 Javascript
JS实现点击文字对应DIV层不停闪动效果的方法
2015/03/02 Javascript
莱鸟介绍window.print()方法
2016/01/06 Javascript
JS跨域请求的问题解析
2018/12/03 Javascript
JavaScript读取本地文件常用方法流程解析
2020/10/12 Javascript
js中复选框的取值及赋值示例详解
2020/10/18 Javascript
深入理解Python装饰器
2016/07/27 Python
Python中Selenium模拟JQuery滑动解锁实例
2017/07/26 Python
Python排序搜索基本算法之堆排序实例详解
2017/12/08 Python
python 获取毫秒数,计算调用时长的方法
2019/02/20 Python
安装完Python包然后找不到模块的解决步骤
2020/02/13 Python
什么是Python中的顺序表
2020/06/02 Python
如何基于matlab相机标定导出xml文件
2020/11/02 Python
世界上最好的足球商店:Unisport
2019/03/02 全球购物
实习自我鉴定范文
2013/10/30 职场文书
个人求职信范文分享
2013/12/13 职场文书
高中班长自我鉴定
2013/12/20 职场文书
一句话工作感言
2014/03/01 职场文书
决心书范文
2014/03/11 职场文书
感情真挚的毕业生求职信
2014/07/19 职场文书
志愿者服务活动总结报告
2015/05/06 职场文书
2015年妇女工作总结
2015/05/14 职场文书
《风不能把阳光打败》读后感3篇
2020/01/06 职场文书
基于Python实现西西成语接龙小助手
2022/08/05 Golang