python基于Selenium的web自动化框架

news2024/12/24 10:15:46

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 自动化测试框架

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

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

  • 用例管理模块

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

  • 自动化执行控制器

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

  • 报表生成模块

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

  • 日志模块

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

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

3.1 需求分析

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

3.2 概要设计

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

概要设计类图:

3.3 详细设计与实现

3.3.1 页面管理

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

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

页面模式的类图如下:

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

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

页面基类BasePage.py:

1

2

3

4

5

6

class BasePage(object):

  """description of class"""

  

  #webdriver instance

  def __init__(self, driver):

    self.driver = driver

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

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()

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

  • 简单并且清晰

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

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

1

2

3

4

5

#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)

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

1

2

3

4

5

6

#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:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

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)

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

1

2

3

4

5

6

7

8

9

10

11

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类库,以便更方便的控制日志输出:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

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的登录用例如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

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:

1

2

3

4

Test_Login_pass.py

Test_Login_Fail.py

#Test_MainPage_CheckSecurityTableInfo.py

Test_MainPage_EditSecurityInfo.py

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

具体的调用代码如下:

1

2

3

4

5

6

7

8

9

10

11

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附件的形式通过邮件发出,例如:

4 需要改进的模块

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

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

5 总结

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

 最后感谢每一个认真阅读我文章的人,看着粉丝一路的上涨和关注,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走! 希望能帮助到你!【100%无套路免费领取】

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1857370.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

电脑显示msvcp110.dll丢失的修复方法,快速解决msvcp110.dll的5种方法

今天,我想与大家分享一个我在技术支持领域遇到的一个常见问题,以及我如何解决它的经验。这个问题是关于“msvcp110.dll丢失”的修复方法。 一,了解msvcp110.dll是什么 msvcp110.dll 是一个动态链接库文件,它是 Microsoft Visual …

CSRF代码审计

1 CSRF漏洞 1.1 漏洞原理 跨站请求伪造(Cross-site request forgery)CSRF,是一种使已登录用户在不知情的情况下执行某种动作的攻击。因为攻击者看不到伪造请求的响应结果,所以CSRF攻击主要用来执行动作,而非窃取用户…

Go 在结构体中定义下划线(_)字段原来还有这个特殊用途?

作者:陈明勇 个人网站:https://chenmingyong.cn 文章持续更新,如果本文能让您有所收获,欢迎点赞收藏加关注本号。 微信阅读可搜《程序员陈明勇》。 这篇文章已被收录于 GitHub https://github.com/chenmingyong0423/blog&#xff…

BADI - 采购申请增强ME_PROCESS_REQ_CUST行项目增强PROCESS_ITEM

需求:如果行项目有文本,则修改行项目 实现步骤 使用事务代码 SE19或SE18 为 BADI(ME_PROCESS_REQ_CUST )创建实现。单击方法PROCESS_ITEM使用方法 IF_LONGTEXTS_MM~GET_TEXTOBJECT 获取订单项文本对象 ID。使用方法 IF_LONGTEXTS…

粉末冶金5G智能工厂工业物联数字孪生平台,推进制造业数字化转型

粉末冶金5G智能工厂工业物联数字孪生平台,推进制造业数字化转型。在数字化浪潮席卷全球的今天,制造业的数字化转型已然成为不可逆转的趋势。粉末冶金行业,作为制造业的重要一环,亦需紧跟时代步伐,以5G智能工厂、工业物…

elementUI的衍生组件,avue的crud表格错位问题

问题描述: 每次从别的页面跳转回来就发现表格显示错位了 一通查 结果发现是有两层表格 解决办法: 根据开发者工具中看到的样式选择器,很粗暴的在全局样式文件中加一个: 效果:

全网最详细的SpringBoot管理系统开发教程

此文章适用于 学生管理系统、成绩管理系统、在线考试系统、图书管理系统 等,提供源码下载。 技术架构:Java SpringBoot Vue3 MySQL 一、项目搭建 1.1 开发工具 2024年了,我们就不考虑Eclipse了好吧,直接下载IDEA社区版。下载…

java医院绩效考核系统源码:医院绩效考核的发展趋势、医院绩效考核的具体方法

java医院绩效考核系统源码:医院绩效考核的发展趋势、医院绩效考核的具体方法 医疗机构绩效考核是对医疗机构绩效进行评估和分析的一项重要工作。它对医疗机构的发展起到了重要的指导和推动作用。本文将会分析国际上医院绩效考核的发展趋势以及医院绩效考核的具体方…

全国青少年信息素养大赛图形化编程复赛小高组真题-附答案

2023年全国青少年信息素养大赛图形化编程复赛小高组真题 题目总数:6 总分数:100 编程题 第 1 题 问答题 编程实现: 按下空格,背景会随机变化,如果变成沙漠背景,骆驼就会出现。 具体要求&#x…

【C++】继承(定义、菱形继承、虚拟继承)

🌈个人主页:秦jh_-CSDN博客🔥 系列专栏:https://blog.csdn.net/qinjh_/category_12575764.html?spm1001.2014.3001.5482 ​ 目录 继承的概念 继承定义 定义格式 继承关系和访问限定符 继承基类成员访问方式的变化 基类和…

【WEB前端2024】3D智体编程:乔布斯3D纪念馆-第46课-使用json文件

【WEB前端2024】3D智体编程:乔布斯3D纪念馆-第45课-使用头像 使用dtns.network德塔世界(开源的智体世界引擎),策划和设计《乔布斯超大型的开源3D纪念馆》的系列教程。dtns.network是一款主要由JavaScript编写的智体世界引擎&…

Java版ERP管理系统源码解析:利用Spring Cloud Alibaba和Spring Boot实现微服务架构

在当今数字化浪潮的推动下,企业对于高效、稳定且易于扩展的管理系统需求日益增长。为了满足这一需求,我们精心打造了一款基于Java技术的鸿鹄ERP(Enterprise Resource Planning)管理系统。该系统充分利用了Spring Cloud Alibaba、S…

C# Web控件与数据感应之数据返写

目录 关于数据返写 准备视图 范例运行环境 ControlInducingFieldName 方法 设计与实现 如何根据 ID 查找控件 FindControlEx 方法 调用示例 小结 关于数据返写 数据感应也即数据捆绑,是一种动态的,Web控件与数据源之间的交互,数据…

达梦数据守护集群脑裂恢复

集群环境参考上篇 达梦数据守护集群部署 https://blog.csdn.net/qq_25045631/article/details/139900164 集群发散脑裂时,监视器显示如下,实例GRP1_RT_01发生脑裂 1. 关闭DW环境 因为Global守护类型的守护进程,会自动将数据库实例切换到O…

MySQL集群高可用架构之MySQL InnoDB Cluste

今天我将详细的为大家介绍Centos 7.5 基于 MySQL 5.7的 InnoDB Cluster 多节点高可用集群环境部署的相关知识,希望大家能够从中收获多多!如有帮助,请点在看、转发支持一波!!! 一、MySQL InnoDB Cluster 介…

【 华为OD机试】信号发射和接收(C++ Java JavaScript Python)

题目 题目描述 有一个二维的天线矩阵,每根天线可以向其他天线发射信号,也能接收其他天线的信号,为了简化起见,我们约定每根天线只能向东和向南发射信号,换言之,每根天线只能接收东向或南向的信号。 每根天线有自己的高度anth,每根天线的高度存储在一个二维数组中,各个天…

Web框架简介

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 如果你要从零开始建立了一些网站,可能会注意到你不得不反复解决一些类似的问题。这样做是令人厌烦的,并且违反了良好编程的核…

【系统架构设计师】四、嵌入式基础知识(软件|软件设计|硬件|式总线逻辑)

目录 一、嵌入式软件 1.1 嵌入式软件分类 1.2 板级支持包(BSP) 1.3 BootLoader 1.4 设备驱动程序 二、嵌入式软件设计 2.1 编码 2.2 交叉编译 2.3 交叉调试 三、嵌入式系统硬件的分类 3.1 根据用途分类 3.2 存储器分类 四、内(外)总线逻辑 …

苹果Mac系统安装adobe软件“无法打开install因为无法验证开发者”解决方法

对于大部分小伙伴,特别是从事视频后期、设计等专业的人来说,Adobe全家桶系列软件,相信都或多或少用过,比如Photoshop、Premiere、illustrator、Lightroom等等。这些软件不仅支持Windows系统,也完美适配于苹果Mac系统&a…

AcWing算法基础课笔记——动态规划之背包问题

背包问题 1. 01背包问题 解题思路&#xff1a; 题目 2. 01背包问题 - AcWing题库 代码 优化前&#xff1a; #include<iostream> #include<algorithm>using namespace std;const int N 1010;int n, m; int v[N], w[N]; int f[N][N];int main() {cin >> …