zoukankan      html  css  js  c++  java
  • Selenium Web自动化测试——基于unittest框架的PO设计模式

      引言

      前面一直在讲接口自动化测试框架与案例分享,很少讲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设计模式核心组件

      画一个操作如下:

      

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

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

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

      核心组件:

      BasePage.py模块:

    class BasePage(object):
        def __init__(self,driver):
            self.driver = driver
    
        pass
    

      Page页面模块:

    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用例模块:

    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:

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

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

    #!/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()
    

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

      

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

    C:UsersAdministratorDesktopDemo_PO
    C:UsersAdministratorDesktopDemo_POReport
    .
    Time Elapsed: 0:00:13.370322
    
    Process finished with exit code 0

      总结

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

  • 相关阅读:
    基于shell脚本比较数字加减乘除 要bc计算器
    基于shell脚本比较数字大小
    备份WordPress
    在CentOS 7 安装没有mysql
    SQL中判断字符串中包含字符的方法
    ASP.NET生成的HTML代码
    win7禁用休眠,献给c盘空间不足的朋友.
    SQLServer2005和2008的分页技术比较[转]
    浏览器兼容性系列--浅谈window.attachEvent
    在ASP.NET 的服务器端控件中有三种关于 ID 的属性
  • 原文地址:https://www.cnblogs.com/liudinglong/p/14021596.html
Copyright © 2011-2022 走看看