Selenium Web自动化测试——基于unittest框架的PO设计模式

news2024/11/20 2:30:08

 🔥 交流讨论:欢迎加入我们一起学习!

🔥 资源分享耗时200+小时精选的「软件测试」资料包

🔥 教程推荐:火遍全网的《软件测试》教程  

📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!

引言

  前面一直在讲接口自动化测试框架与案例分享,很少讲Selenium这个Web自动化测试神器。它主要用来做UI自动化测试,大家都知道UI自动化测试成本相当高,一般的Web自动化测试我是一直不建议做的。

  虽然不推荐,但是这里有一个设计思想是不错的——PO设计模式。

  PO设计模式

  PO设计模式,英文名称:Page Object Model。PO设计模式是Selenium自动化测试中最佳的设计方式之一。相比传统设计中:页面定位元素→输入数据→操作元素→断言结果,会有以下问题:

  1、易用性差:杂乱无章的定位元素方法,例如:find_element;

  2、扩展性不好:用例孤立,无法扩展;

  3、复用性差:无公共方法,很难服用;

  4、可维护性差:一旦元素变化或测试步骤变化,需要维护大量代码和用例;

  针对上面一些弊端,做了一些优化:

  POM设计模式,将页面定位和业务操作分开,将元素定位和测试方法分离,从而提高代码的维护性。而传统的POM是元素定位和测试方法放在一起,如下图:

  

  这样做的优势:

  1、页面元素定位和业务操作方法分离,使得代码更加清晰,减少冗余代码;

  2、测试方法单独抽离,这样提高用例的可读性;

  3、针对ui变化频繁的项目和测试步骤的变化,提高了测试用例的维护性;

  一条测试用例可能需要多个步骤操作元素,将每一个步骤单独封装成一个方法,在执行测试用例时调用封装好的方法进行操作。PO模式可以把一个页面分为三个层级,对象库层、操作层、业务层。

  对象库层:封装定位元素的方法。

  操作层:封装对元素的操作。

  业务层:将一个或多个操作组合起来完成一个业务功能。

  PO设计模式核心组件

  画一个操作如下:

  

   PO的核心要素:

  1. 在 PO 模式中抽离封装集成一个 BasePage 类,该基类应该拥有一个只实现 webdriver 实例的属性

  2. 每一个page 都继承BasePage,通过 driver 来管理 page 中元素,将 page 中的操作封装成一个个方法

  3. TestCase 继承 unittest.TestCase类,并依赖page类,从而实现相应的测试步骤

   首先抽象封装一个BasePage类,这个基类拥有Webdriver实例的属性,将页面分成一个个Page,每一个Page继承基类BasePage,可以通过driver来管理每一个Page中的元素,

  在Page中将定位元素的操作封装成一个一个方法。TestCase继承unittest里面的TestCase类,并且依赖Page类,进行测试步骤的执行工作。

  这样以来,页面元素一旦变化,只需要维护每一个Page中的方法,测试流程发生变化,只需要维护TestCase即可。

  核心组件:

  BasePage.py模块:

1

2

3

4

5

class BasePage(object):

    def __init__(self,driver):

        self.driver = driver

    pass

  Page页面模块:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

from SeleniumProject.PO.BasePage import BasePage

class LoginBase(BasePage):

    # 定位元素,括号中是通过find_element来获取元素的属性

    uname = ()

    pwd = ()

    def set_uname(self,uname):

        name =self.driver.find_element(*LoginBase.uname)

        name.send_keys("用户名")

    def set_pwd(self,pwd):

        password = self.driver.find_element(*LoginBase.pwd)

        password.send_keys("密码")

    pass

  TestCase用例模块:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

from unittest import TestCase

import unittest

from selenium import webdriver

class Test_Login(TestCase):

    def setUp(self):

        self.driver = webdriver.Chrome()

        self.driver.get("https://cn.bing.com/")

    # 测试步骤

    def test_Login(self):

        self.driver.get(self.base_url)

        pass

    def tearDown(self):

        self.driver.quit()

if __name__ == "__main__":

    unittest.main()

  

  PO模式简单实例

  现在根据PO设计模式思想,简单实现一个需求:

  打开浏览器,输入url:https://www.baidu.com,在百度搜索文本框内输入关键字:selenium,然后单击:百度一下,进行搜索。

  根据需求,设计步骤如下:

  BasePage:

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

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

#!/usr/bin/env python

# -*- coding: utf-8 -*-

'''

# @Time    : 2020/11/22 0022 16:07

# @Author  : liudinglong

# @File    : basepage.py

# @Description:

# @Question:

'''

from selenium import webdriver

from selenium.webdriver.support.wait import WebDriverWait

from selenium.webdriver.support import expected_conditions as EC

class BasePage(object):

    def __init__(self,driver,url):

        """

        @param driver:

        @param base_url:

        """

        self.dr = driver

        self.base_url =  url

    # 定义私有方法,类对象和子类可以访问

    def _open(self,url):

        self.dr.get(url)

        self.dr.maximize_window()

    # 定义open方法,调用_open方法

    def open(self):

        self._open(self.base_url)

    def find_emelemt(self,*loc):

        try:

            WebDriverWait(self.dr,10).until(EC.visibility_of_all_elements_located(loc))

            return self.dr.find_element(*loc)

        except:

            print("页面中没有%s元素"%(self.loc))

    # 定义script()方法,用于执行JS脚本,比方上上传文件啥的

    def script(self, src):

        self.dr.excute_script(src)

        # 定义页面跳转方法,比方说有的页面有frame嵌套

    def switch_frame(self, loc):

        return self.dr.switch_to_frame(loc)

        # 重新定义send_keys()方法,为了保证搜索按钮是否存在,还有有的输入框中默认有值,要清空

    def send_keys(self, loc, value, clear_first=True, click_first=True):

        try:

            # getattr方法相当于实现了self.loc

            loc = getattr(self, "_%s" % loc)

            # 是否存在搜索按钮

            if click_first:

                self.find_emelemt(*loc).click()

            # 清空搜索框中的值,并输入需要搜索的值

            if clear_first:

                self.find_emelemt(*loc).clear()

                self.find_emelemt(*loc).send_keys(value)

        except:

            print("页面上未找到%s元素" % (self.loc))

  SearchPage:

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

#!/usr/bin/env python

# -*- coding: utf-8 -*-

'''

# @Time    : 2020/11/22 0022 18:38

# @Author  : liudinglong

# @File    : SearchPage.py

# @Description:

# @Question:

'''

from selenium.webdriver.common.by import By

from Common.basepage import  BasePage

class SearchPage(BasePage):

    # 定位元素

    search_loc = (By.ID,"kw") #搜索框

    btn_loc = (By.ID,"su")    #搜索按钮

    # 重写父类的open()方法

    def open(self):

        self._open(self.base_url)

    def search_content(self,content):

        # 调用父类的find_emelemt,然后将本类的参数传入

        content1 =  self.find_emelemt(*self.search_loc)

        content1.send_keys(content)

    def btn_click(self):

        btn1 = self.find_emelemt(*self.btn_loc)

        btn1.click()

  TestCase:

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

#!/usr/bin/env python

# -*- coding: utf-8 -*-

'''

# @Time    : 2020/11/22 0022 18:40

# @Author  : liudinglong

# @File    : test_001.py

# @Description:

# @Question:

'''

from unittest import TestCase

import unittest

from selenium import webdriver

from time import sleep

from Page.searchpage import SearchPage

class CaseRun(TestCase):

    def setUp(self):

        self.driver = webdriver.Chrome()

        self.url = "https://www.baidu.com"

        sleep(3)

        self.content = "selenium"

    # 测试步骤

    def test_search(self):

        bing_page = SearchPage(self.driver,self.url)

        bing_page.open()

        bing_page.search_content(self.content)

        try:

            bing_page.btn_click()

            sleep(3)

            print("查询成功")

        except Exception as Error:

            print(Error)

    def tearDown(self):

        self.driver.quit()

if __name__ == "__main__":

    unittest.main()

  三个核心组件完成,项目结构如下:

  

   运行测试,生成报告如下:

1

2

3

4

5

6

C:\Users\Administrator\Desktop\Demo_PO

C:\Users\Administrator\Desktop\Demo_PO\Report

.

Time Elapsed: 0:00:13.370322

Process finished with exit code 0

总结

这个Demo很简单,主要意图是帮助理解PO设计模式的思想,如果需要代码,可以加入QQ群:785128166 ,我们这里主要是进行自动化测试和测试开发学习与沟通交流,如果其他意图请绕行~

作为一个软件测试的过来人,我想尽自己最大的努力,帮助每一个伙伴都能顺利找到工作。所以我整理了下面这份资源,现在免费分享给大家,有需要的小伙伴可以关注【公众号:程序员二黑】自提!

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

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

相关文章

回归测试,有什么高效的测试方法?

什么是回归测试? 回归测试(Regression testing) 指在发生修改之后重新测试先前的测试以保证修改的正确性。理论上,软件产生新版本,都需要进行回归测试,验证以前发现和修复的错误是否在新软件版本上再次出现…

虚拟机Centos8登陆之后又弹回到登陆界面

今天开机发行,虚拟机Centos登陆之后又弹回到登陆界面,在网上一通搜索之后,发现Centos7正确输入账号密码后,循环进入输入账号密码界面(情况一设置错误PATH),以及进入单用户模式_centos一直卡在用…

120.PyQt5_QPainter_图形绘制控件

我 的 个 人 主 页:👉👉 失心疯的个人主页 👈👈 入 门 教 程 推 荐 :👉👉 Python零基础入门教程合集 👈👈 虚 拟 环 境 搭 建 :👉&…

第6章:6.1 文本格式化 (MATLAB入门课程)

讲解视频:可以在bilibili搜索《MATLAB教程新手入门篇——数学建模清风主讲》。​ MATLAB教程新手入门篇(数学建模清风主讲,适合零基础同学观看)_哔哩哔哩_bilibili 在数据处理与报告生成的过程中,我们经常需要将数据转…

AI“造神运动”终结,杀死,重生

AGI回归本质,百亿美金创业机会涌现。 “专注AI技术迭代会让我焦虑,关注业务我不会焦虑,有些问题十年前存在十年后还在,我现在就明确不卷模型,只思考如何让产品能自我‘造血’。” 一位正卷在AI创业洪流里的硅谷创业者…

重学SpringBoot3-WebMvcConfigurer接口

更多SpringBoot3内容请关注我的专栏:《SpringBoot3》 期待您的点赞👍收藏⭐评论✍ 重学SpringBoot3-WebMvcConfigurer接口 WebMvcConfigurer基本信息为什么WebMvcConfigurer能配置底层行为实现WebMvcConfigurer举例1. 自定义格式化器和转换器2. 添加拦截…

AI新工具(20240313) 世界上第一个完全自主的AI软件工程师Devin 已经开始接外包了;又一个开源本地知识库问答系统

1: Cognition AI Devin 世界上第一个完全自主的AI软件工程师Devin 已经开始接外包了 Devin是世界上第一个完全自主的AI软件工程师,具备长期推理和规划的能力,能够执行复杂的工程任务,包括学习、修复错误、与用户实时合作等。Devin可以学习并…

200W年薪的大佬 随手丢给我“Spring速成宝典”看完这些知识点直接定级P7

面试官:答的很好,最后一个问题:如果没有Spring,你打算怎么开展工作? 这个思考了几分钟后,程序员小建是这么回答的:Spring的核心源码,比如:IOC、AOP、Spring事务、MVC原理…

土木工程设计系列-基于规范的简单基础工程自动设计

土木工程设计系列-基于规范的简单基础工程自动设计 土木工程设计系列-基于规范的简单基础工程自动设计 土木工程设计系列-基于规范的简单基础工程自动设计前言基础工程设计简介程序组成源码文件树文件说明:程序执行流程 部分接口介绍程序使用源码获取 前言 本文为土…

高精度三维扫描测量服务3d扫描仪抄数工业级精密激光扫描建模设计

在工业设计与制造领域,工业3D扫描仪的应用日益广泛,其“抄数设计”的功能更是备受瞩目。抄数设计,简单来说,就是通过3D扫描仪对实物进行精确测量,快速获取其三维数据,并基于这些数据进行设计、分析和优化。…

快速排序 刷题笔记

思路 分治双指针 在每个区间选定一个基准目标 两个指针从数组的两边向中间推进 使用 while循环判断 do {i;}while(q[i]<x); do{j--;}while(q[j]>x); 每次这样做完就会找到q[i]>x,,,,q[j]小于x 此时我们交换 q[i] ,q[j]于是小于x的数分到了小于x的一侧 大…

离散化算法,以Acwing802.区间和为例子(C++实现)

目录 1.例题2.算法实现思路3.代码 1.例题 假定有一个无限长的数轴&#xff0c;数轴上每个坐标上的数都是 0现在&#xff0c;我们首先进行 n 次操作&#xff0c;每次操作将某一位置 x 上的数加 c接下来&#xff0c;进行 m 次询问&#xff0c;每个询问包含两个整数 l 和 r&#…

【五】prometheus+grafna初步部署和简单使用

部署Prometheus 地址: https://github.com/prometheus-operator/kube-prometheus/tree/release-0.7 学习来源&#xff1a;https://www.cnblogs.com/lidong94/p/14500276.html、https://juejin.cn/post/6865504989695967245?searchId20240312205710B746697AB0CDB7706DB3 我使用…

人事地震,京东方多名董事离职 | 百能云芯

3月12日&#xff0c;京东方公布了《关于董事辞职的公告》&#xff0c;公告内容显示&#xff1a;董事会于2024年3月12日收到副董事长刘晓东先生、董事孙芸女士提交的书面辞呈。 双方离职原因均系年龄原因&#xff0c;刘晓东先生申请辞去公司董事、副董事长、董事会专门委员会委员…

深入理解Hive:探索不同的表类型及其应用场景

文章目录 1. 引言2. Hive表类型概览2.1 按照数据存储位置2.2 按照数据管理方式2.3 按照查询优化2.4 按照数据的临时性和持久性 3. 写在最后 1. 引言 在大数据时代&#xff0c;Hive作为一种数据仓库工具&#xff0c;为我们提供了强大的数据存储和查询能力。了解Hive的不同表类型…

原理+代码:Diffusion Model 直观理解

原理部分 直观理解 数学形式 网络如何训练&#xff1f; 训练一个怎样的网络&#xff1f; 代码部分 Network helpers Positional embeddings ResNet/ConvNeXT block Attention module Conditional U-Net 定义前向扩散过程 用一个实例来说明前向加噪过程 损失函数 …

基于SSH框架的电子商城的设计

目录 摘要 2 Abstract 3 第一章 前言 4 1.1 课题研究意义 4 1.2 国外研究现状 4 方案一&#xff1a; 4 方案二&#xff1a; 4 方案三&#xff1a; 5 1.3 课题研究内容 5 &#xff08;1&#xff09;商品浏览模块 5 &#xff08;2&#xff09;订单管理模块 5 &#xff08;3&…

基于 llvm 3.4 的C++重构工具

还未测试&#xff0c;存个档&#xff0c;未完待续 1,源码 Makefile LLVM_CONFIG?llvm-configifndef VERBOSE QUIET: endifSRC_DIR?$(PWD) LDFLAGS$(shell $(LLVM_CONFIG) --ldflags) COMMON_FLAGS-Wall -Wextra CXXFLAGS$(COMMON_FLAGS) $(shell $(LLVM_CONFIG) --cxxflags…

【机器学习300问】36、什么是集成学习?

一、什么是集成学习&#xff1f; &#xff08;1&#xff09;它的出现是为了解决什么问题&#xff1f; 提高准确性&#xff1a;单个模型可能对某些数据敏感或者有概念偏见&#xff0c;而集成多个模型可以提高预测的准确性。让模型变稳定&#xff1a;一些模型&#xff0c;如决策…

【JavaScript】数据类型转换 ① ( 隐式转换 和 显式转换 | 常用的 数据类型转换 | 转为 字符串类型 方法 )

文章目录 一、 JavaScript 数据类型转换1、数据类型转换2、隐式转换 和 显式转换3、常用的 数据类型转换4、转为 字符串类型 方法 一、 JavaScript 数据类型转换 1、数据类型转换 在 网页端 使用 HTML 表单 和 浏览器输入框 prompt 函数 , 接收的数据 是 字符串类型 变量 , 该…