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

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

🔥 资源分享耗时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 ,我们这里主要是进行自动化测试和测试开发学习与沟通交流,如果其他意图请绕行~

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

一文讲懂 C++ 类和对象(1)

0. 面向过程程序设计和面向对象程序设计的区别 面向对象程序设计往往关注的是怎么去做,是将解决问题的步骤分析出来,然后用函数把步骤一步一步实现,然后再依次调用就可以了。而面向对象是将构成问题的事物,分解成若干个对象&…

比特币创造历史新纪录

综合来源:coindesk and cointelegraph 编译:秦晋 3月11日,比特币在亚洲交易时段首次突破71,000美元,这个是比特币创造的价格新纪录。自1月11日比特币现货ETF在美国获批以来,比特币一直在稳步上涨。以太币突破4000美元。…

rust学习(手动写一个线程池)

哈哈&#xff0c;主要是为了练习一下rust的语法&#xff0c;不喜勿喷。 一.Executor申明 struct AExecutor<T> {results:Arc<Mutex<HashMap<u32,T>>>, //1functions:Arc<Mutex<Vec<ATask<T>>>> //2 } 1.results&#xff1a…

产品测试方案:视频接入平台并发性能测试方案和报告(即150路视频并发流媒体服务器模块的性能测试方案和报告)

目 录 一、测试目的&#xff1a; 二、测试方案&#xff1a; 2.1、测试思路 2.2、拓扑图 三、测试环境 3.1 服务器配置 3.2 网络摄像机列表 3.3 测试软件 四、测试流程 4.1 H.264并发测试&#xff1a; 4.1.1老版本srsout3.10并发测试 4.1.2 新版本srsout…

2024年信息技术与计算机工程国际学术会议(ICITCEI 2024)

2024年信息技术与计算机工程国际学术会议&#xff08;ICITCEI 2024&#xff09; 2024 International Conference on Information Technology and Computer Engineering ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 大会主题&#xff1a; 信息系统和技术…

如何选择腾讯云轻量应用服务器地域?北京上海广州哪个合适?

腾讯云轻量应用服务器地域如何选择&#xff1f;地域就近选择&#xff0c;北方选北京地域、南方选广州地域&#xff0c;华东地区选上海地域。广州上海北京地域有什么区别&#xff1f;哪个好&#xff1f;区别就是城市地理位置不同&#xff0c;其他的差不多&#xff0c;不区分好坏…

基于java+springboot+vue实现的停车场管理系统(文末源码+Lw)23-258

摘 要 如今社会上各行各业&#xff0c;都喜欢用自己行业的专属软件工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。新技术的产生&#xff0c;往往能解决一些老技术的弊端问题。因为传统停车场管理系统信息管理难度大&#xff0c;容错率低&…

python实现卡普均值最小回路算法

如果给定一个含有环的有向图&#xff0c;要在这个图中找出所有的环并计算这些环的路径长度&#xff0c;然后除以环的边数&#xff0c;所得到的结果也就是环的平均值&#xff0c;这里也就是如何计算这个环的最小均值问题。 首先可以确定的是&#xff0c;如果图中均值最小的环的…

漏洞发现-漏扫项目篇武装BURP浏览器插件信息收集分析辅助

知识点 1、插件类-武装BurpSuite-漏洞检测&分析辅助 2、插件类-武装谷歌浏览器-信息收集&情报辅助 章节点&#xff1a; 漏洞发现-Web&框架组件&中间件&APP&小程序&系统 扫描项目-综合漏扫&特征漏扫&被动漏扫&联动漏扫 Poc开发-Ymal语…

深入浅出落地应用分析:AI虚拟数字人

据艾媒咨询,2025年中国虚拟人市场规模预计达480.6亿元,用户群体主要为中型及小微型企业,产品需求量TOP5分别是电商、卫生、社会保障和社会福利业、教育、金融和运输业,主要产品类型为数字员工及定制化数字人。 一、什么是数字人 1.1 概念介绍 数字人是指以数字形式存在于…

2024年腾讯云轻量服务器地域选择方法_新手地域教程

腾讯云轻量应用服务器地域如何选择&#xff1f;地域就近选择&#xff0c;北方选北京地域、南方选广州地域&#xff0c;华东地区选上海地域。广州上海北京地域有什么区别&#xff1f;哪个好&#xff1f;区别就是城市地理位置不同&#xff0c;其他的差不多&#xff0c;不区分好坏…

代码贴--链表--数据机构

本博客将记录链表代码(单链表)&#xff0c;后续其他链表和其他数据结构内容请看我的其他博客 头文件(SList.h) #pragma once #include<iostream> #include<bits/stdc.h> using namespace std;typedef int SLTDataType;struct SListNode {int data;struct SListNo…

每日OJ题_牛客HJ37 统计每个月兔子的总数(IO型OJ)

目录 牛客HJ37 统计每个月兔子的总数 解析代码 牛客HJ37 统计每个月兔子的总数 统计每个月兔子的总数_牛客题霸_牛客网 解析代码 #include <iostream> #include <vector>using namespace std; int main() {int n 0;cin >> n;vector<int> arr(n 1…

VRay渲染动画怎么快一点?提升VRay动画渲染方法

随着动画和视觉效果行业对高品质渲染的需求日益增长&#xff0c;V-Ray作为一款领先的渲染工具&#xff0c;面临着提升渲染效率的挑战。项目规模和复杂度的扩大导致渲染时间延长&#xff0c;对交付期限造成影响。探索加速V-Ray渲染流程的方法变得尤为关键。 一、动画渲染的常见瓶…

docker-compose up -d使用遇到问题no configuration file provided: not found

docker-compose up -d使用遇到问题&#xff0c;因为你文件名称没指定&#xff0c; 又找不到默认的文件名称&#xff1b;如果该目录下有个文件叫docker-compose.yml时&#xff0c;那么可以直接使用docker-compose up -d;否则就要使用docker-compose -f mysql up -d

4、设计模式之建造者模式(Builder)

一、什么是建造者模式 建造者模式是一种创建型设计模式&#xff0c;也叫生成器模式。 定义&#xff1a;封装一个复杂对象构造过程&#xff0c;并允许按步骤构造。 解释&#xff1a;就是将复杂对象的创建过程拆分成多个简单对象的创建过程&#xff0c;并将这些简单对象组合起来…

Vue.js过滤器:让数据展示更灵活

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

认识与理解java中的stream流

系列文章目录 1.SpringBoot整合RabbitMQ并实现消息发送与接收 2. 解析JSON格式参数 & 修改对象的key 3. VUE整合Echarts实现简单的数据可视化 4. List&#xff1c;HashMap&#xff1c;String,String&#xff1e;&#xff1e;实现自定义字符串排序&#xff08;key排序、Val…

【LeetCode: 2864. 最大二进制奇数 + 模拟 + 位运算】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

HarmonyOS NEXT应用开发之深色跑马灯案例

介绍 本示例介绍了文本宽度过宽时&#xff0c;如何实现文本首尾相接循环滚动并显示在可视区&#xff0c;以及每循环滚动一次之后会停滞一段时间后再滚动。 效果图预览 使用说明&#xff1a; 1.进入页面&#xff0c;检票口文本处&#xff0c;实现文本首尾相接循环滚动&#x…