web自动化测试思路及实战(2):web自动化测试思路及实战

web自动化测试思路及实战

全部代码-Gitee: 

Web_Auto_Test_FrameWork: WEB自动化测试实战练习 (gitee.com)

前期准备工作:

1、 python  列表、字典、循环、面向对象 (python语言基础)

2、 python第三方模块的使用:

       os  time 、文件操作、日期查找、配置文件ini、log日志、excel (框架底层的工具)

3、selenium入门:驱动、浏览器、元素识别、元素操作(页面元素识别、元素操作)

4、unittest框架:(优化成4章)框架的流程、断言、忽略测试、构建套件、测试报告生成(测试用例层、执行层)

5. selenium+unittest实战:编写线性脚本框架,

6. 正式开始编写PO模式的UI自动化框架

pageobjects 设计模式

Page  Objects概念:        

          Page  Objects是指UI界面上用于与用户进行交互的对象

pageobjects 设计模式概念:

         pageobjects 模式是Selenium中的一种测试设计模式,主要是将每一个页面设计为一个Class,其中包含页面中需要测试的元素(按钮,输入框,标题 等),这样在Selenium测试页面中可以通过调用页面类来获取页面元素,这样巧妙的避免了当页面元素id或者位置变化时,需要改测试页面代码的情况。 当页面元素id变化时,只需要更改测试页Class中页面的属性即可。

         简单来讲,就是将代码以页面为单位进行组织,针对这个页面上的所有信息,相关操作都放到一个类中;从而使具体的测试用例变成了简单的调用和验证操作

pageobjects 设计模式:把页面设计成类,把页面上的控件作为属性,把控件的操作设计成方法

类=属性+方法

以下是以关键字数据驱动框架做的实战项目

被测系统:禅道系统

框架01-pageobjects模型基础及应用

思路:

1. 新建一个元素信息包element_info,存放我们的页面

2. 新建webdriver驱动文件夹,导入浏览器的驱动文件

3. 新建login_page.py,添加属性、方法

4. 新建一个main_page.py

1.  在项目名下新建一个python的文件夹element_infos

新建Web_Auto_Test_Framework项目,在该项目的下面新建element_infos的py文件夹

2.  新建webdriver驱动文件夹,导入浏览器驱动文件

3.  在element_infos下新建login_page.py,添加属性、方法

前置条件下载selenium库:

代码示例:

# encoding: utf-8
# @author: Jeffrey
# @file: login_page.py
# @time: 2022/7/17 21:39
# @desc: 页面是类  控件:属性  控件操作:方法
import os
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service

current_path = os.path.dirname(__file__)
driver_path = os.path.join(current_path, '../webdriver/chromedriver.exe')


class LoginPage:

    def __init__(self):
        chrome_driver_path = Service(driver_path)
        self.driver = webdriver.Chrome(service=chrome_driver_path)
        self.driver.get('http://47.107.178.45/zentao/www/index.php?m=user&f=login')
        self.driver.maximize_window()
        self.driver.implicitly_wait(30)

        # 控件:属性
        self.username_input = self.driver.find_element(By.XPATH,'//input[@name="account"]')
        self.password_input = self.driver.find_element(By.XPATH,'//input[@name="password"]')
        self.keep_login_box = self.driver.find_element(By.XPATH,'//input[@id="keepLoginon"]')
        self.login_button = self.driver.find_element(By.XPATH,'//button[@id="submit"]')


    # 输入用户名
    def input_username(self,username):
        self.username_input.send_keys(username)
    # 输入密码
    def input_password(self,password):
        self.password_input.send_keys(password)
    # 点击保持登录
    def keep_login(self):
        self.keep_login_box.click()
    # 点击登录按钮
    def click_login(self):
        self.login_button.click()



if __name__ == '__main__':
    loginpage = LoginPage()
    loginpage.input_username('test01')
    loginpage.input_password('newdream123')
    loginpage.click_login()

# 小结:页面=属性+方法
# 属性的命名:名称_元素类型
# 操作的命名:动作_元素名称

4.  新建一个main_page.py

思路:

1. 添加元素--属性

2. 添加动作--方法

3. 处理好driver,把login的driver 转给主页面

代码示例:

#第二个页面
# encoding: utf-8
# @author: Jeffrey
# @file: main_page.py
# @time: 2022/7/2 13:04
# @desc:
import os
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from element_infos.login_page import LoginPage


class MainPage:

    def __init__(self):

        login_page = LoginPage()
        login_page.input_username('test01')
        login_page.input_password('newdream123')
        login_page.click_login()
        # 把登录页面的driver转移到main_page
        self.driver = login_page.driver

        self.companyname_showbox = self.driver.find_element(By.XPATH, '//h1[@id="companyname"]')
        self.myzone_menu = self.driver.find_element(By.XPATH, '//li[@data-id="my"]')
        self.product_menu = self.driver.find_element(By.XPATH, '//li[@data-id="product"]')
        self.username_showbox = self.driver.find_element(By.XPATH, '//span[@class="user-name"]')

    # 获取公司名称
    def get_companyname(self):
        value = self.companyname_showbox.get_attribute('title')
        return value

    # 选择我的地盘
    def goto_myzone(self):
        self.myzone_menu.click()

    # 选择我的产品
    def goto_product(self):
        self.product_menu.click()

    # 获取登录的用户名
    def get_username(self):
        value = self.username_showbox.text
        return value

if __name__ == '__main__':
    main_page = MainPage()
    print(main_page.get_username())

    # pageobject模式是实例化页面之后,需要识别所有的元素,然后再去操作,导致有些元素不能识别的问题;如下三个方法会报错,可参考框架02
    # print(main_page.get_companyname())
    # main_page.goto_myzone()
    # # main_page.goto_product()

5、新建common的py文件夹再添加日志封装log_utils.py

前置条件:在项目根目录下新建一个logs的文件夹

代码示例:

import time
import os
import logging
currrent_path = os.path.dirname(__file__)
log_path = os.path.join(currrent_path,'../logs')


class LogUtils:

    def __init__(self,log_path=log_path):
        self.logfile_path = log_path
        # 创建日志对象logger
        self.logger = logging.getLogger(__name__)
        # 设置日志级别
        self.logger.setLevel(level=logging.INFO)
        # 设置日志的格式
        formatter = logging.Formatter('%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s')

        """在log文件中输出日志"""
        # 日志文件名称显示一天的日志
        self.log_name_path = os.path.join(self.logfile_path, "UI_Test_%s" % time.strftime('%Y_%m_%d'))
        # 创建文件处理程序并实现追加
        self.file_log = logging.FileHandler(self.log_name_path, 'a', encoding='utf-8')
        # 设置日志文件里的格式
        self.file_log.setFormatter(formatter)
        # 设置日志文件里的级别
        self.file_log.setLevel(logging.INFO)
        # 把日志信息输出到文件中
        self.logger.addHandler(self.file_log)
        # 关闭文件
        self.file_log.close()

        """在控制台输出日志"""
        # 日志在控制台
        self.console =logging.StreamHandler()
        # 设置日志级别
        self.console.setLevel(logging.INFO)
        # 设置日志格式
        self.console.setFormatter(formatter)
        # 把日志信息输出到控制台
        self.logger.addHandler(self.console)
        # 关闭控制台日志
        self.console.close()

    # 添加该方法可避免每个日志重复打印两次
    def get_log(self):
        return self.logger

logger = LogUtils().get_log()

if __name__ == '__main__':
    logger.info('123')
   logger.error('error')

# 把封装的日志引入到login_page.py文件中

把日志添加到login_page.py文件中

小结:

1. 封装log_utils工具类

2. 避免一行日志出现多行的情况,在log_utils.py添加了一个logger对象

框架02--pageobject模型优化使用

界面层: 界面的元素

控件层: 单独验证每个控件的功能

功能层:单个或者多个功能组合操作形成的功能

业务层:单个或多个功能形成业务

PO模式分层和线性脚本的比较

线性脚本:按操作识别元素,识别一个元素操作一个,风险比较小

pageobject模式模型:先全部识别元素,然后再进行操作

注意:由于po模式是要先识别全部元素,才能进行操作所以会引出下面的问题

问题引出:

分析:加了这二句之后,出现问题,原因:pageobject模式是实例化页面之后,需要识别所有的元素,然后再去操作,导致有些元素不能识别的问题。

处理方法:识别数据分离,实例化对象的时候不识别元素,操作的时候再去识别。

思路:

1. 在common中创建一个base_page.py-->BasePage类

2. 页面层元素识别方法,改为不用识别好元素,只需要识别到元素的信息

3. 在BasePage类中封装元素识别方法

4. 完善login_page.py中的LoginPage类继承BasePage类,并完善代码

1、在common中创建一个base_page.py-->BasePage类

 

 

 

代码示例:

# encoding: utf-8
# @author: Jeffrey
# @file: base_page.py
# @time: 2022/7/2 17:18
# @desc: 封装页面的公共类和方法
# =============================
# BasePage() 页面基础类
# 作用:把每个页面的公共操作分离出来
# 1、封装浏览器操作
# 2、封装元素是被
# 3、元素操作
# =============================
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from common.log_utils import logger


class BasePage(object):
    """页面基础类"""
    def __init__(self,driver):

        self.driver = driver
        # 该属性是用来编写下方代码时,可以调用find_element方法,编写完后还是要用上面的属性
        # self.driver = webdriver.Chrome(driver)

    def open_url(self,url):
        """打开网站"""
        self.driver.get(url)
        logger.info('打开网站,地址为 %s' % url)

    def set_browser_max(self):
        self.driver.maximize_window()
        logger.info('浏览器窗口最大化')

    def refresh(self):
        """浏览器刷新"""
        self.driver.refresh()
        logger.info('浏览器刷新')

    def get_title(self):
        """获取网页标题"""
        title_value = self.driver.title
        logger.info('获取网页的标题,标题为: %s' % title_value)
        return title_value

    def get_url(self):
        """获取url地址"""
        url_value = self.driver.current_url
        logger.info('获取网站的地址,地址为:%s' % url_value)
        return url_value

    def close_tab(self):
        """关闭当前浏览器的tab页"""
        self.driver.close()
        logger.info('关闭当前浏览器的tab页')

    def exit_driver(self):
        """退出浏览器"""
        self.driver.quit()
        logger.info('退出浏览器')

    """封装等待时间(显式,隐式,固定等待)"""
    def implicitly_wait(self, seconds):
        """封装隐式等待"""
        self.driver.implicitly_wait(seconds)
        logger.info('浏览器隐式等待:%s' % seconds)

    # 固定等待封装
    def wait(self, seconds):
        time.sleep(seconds)
        logger.info('固定等待时间:%s 秒' % seconds)

 

 

2、 在login_page.py页面层元素识别方法,改为不用识别好元素,只需要识别到元素的信息

 

 

 

代码示例:

# encoding: utf-8
# @author: Jeffrey
# @file: login_page.py
# @time: 2022/7/17 21:39
# @desc: 页面是类  控件:属性  控件操作:方法

import os
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from common.log_utils import logger

current_path = os.path.dirname(__file__)
driver_path = os.path.join(current_path, '../webdriver/chromedriver.exe')


class LoginPage:

    def __init__(self):

        logger.info('禅道系统自动化测试')
        chrome_driver_path = Service(driver_path)
        self.driver = webdriver.Chrome(service=chrome_driver_path)
        self.driver.get('http://47.107.178.45/zentao/www/index.php?m=user&f=login')
        logger.info('打开网站:%s' % self.driver.current_url)
        self.driver.maximize_window()
        self.driver.implicitly_wait(30)

        # 控件:属性
        # self.username_input = self.driver.find_element(By.XPATH,'//input[@name="account"]')
        # self.password_input = self.driver.find_element(By.XPATH,'//input[@name="password"]')
        # self.keep_login_box = self.driver.find_element(By.XPATH,'//input[@id="keepLoginon"]')
        # self.login_button = self.driver.find_element(By.XPATH,'//button[@id="submit"]')

        # 用户名输入框
        self.username_inputbox = {
     'element_name': '用户名输入框',
                                  'locator_type': 'xpath',
                                  'locator_value': '//input[@name="account"]',
                                  'timeout': 5}
        # 密码输入框
        self.password_inputbox = {
     'element_name': '密码输入框',
                                  'locator_type': 'xpath',
                                  'locator_value': '//input[@name="password"]',
                                  'timeout': 5}
        # 保持登录
        self.keep_login_box = {
     'element_name': '保持登录按钮',
                               'locator_type': 'xpath',
                               'locator_value': '//input[@id="keepLoginon"]',
                               'timeout': 5}

        # 登录按钮
        self.login_button = {
     'element_name': '登录按钮',
                             'locator_type': 'xpath',
                             'locator_value': '//button[@id="submit"]',
                             'timeout': 5}

    # 输入用户名
    def input_username(self,username):
        self.username_inputbox.send_keys(username)
    # 输入密码
    def input_password(self,password):
        self.password_inputbox.send_keys(password)
    # 点击保持登录
    def keep_login(self):
        self.keep_login_box.click()
    # 点击登录按钮
    def click_login(self):
        self.login_button.click()



if __name__ == '__main__':
    loginpage = LoginPage()
    loginpage.input_username('test01')
    loginpage.input_password('newdream123')
    loginpage.click_login()

# 小结:页面=属性+方法
# 属性的命名:名称_元素类型
# 操作的命名:动作_元素名称

 

 

3、在BasePage类中封装元素识别方法

前置条件:导包

import time

from selenium import webdriver

from selenium.webdriver.common.by import By

from selenium.webdriver.support.wait import WebDriverWait

 

from common.log_utils import logger

 

 

 代码示例:

   # self.username_inputbox = {'element_name': '用户名输入框',
    #                           'locator_type': 'xpath',
    #                           'locator_value': '//input[@name="account"]',
    #                           'timeout': 5}
    # 元素的识别,通过分离处理的元素识别字典信息,返回一个元素
    # self.username_inputbox=self.driver.find_element(By.XPATH,'//input[@name="account"]') #属性-->控件
    # element=WebDriverWait(driver,3).until(lambda x: x.find_element_by_css_selector(div.red_box"))
    def find_element(self, element_info):
        """
        通过元素信息字典数据,查找元素
        :param element_info: 元素识别信息字典
        :return: 返回识别到的元素
        """
        locator_type_name = element_info['locator_type']
        locator_value_info = element_info['locator_value']
        locator_timeout = element_info['timeout']
        if locator_type_name == 'name':
            locator_type = By.NAME
        elif locator_type_name == 'id':
            locator_type = By.ID
        elif locator_type_name == 'xpath':
            locator_type = By.XPATH
        elif locator_type_name == 'css':
            locator_type = By.CSS_SELECTOR
        elif locator_type_name == 'class':
            locator_type = By.CLASS_NAME
        element = WebDriverWait(self.driver, locator_timeout).until(lambda x:x.find_element(locator_type,locator_value_info))
        logger.info('[%s]元素识别成功' % element_info['element_name'])
        return element

 

 

BasePage类:浏览器操作、元素识别、元素操作(切框架、切句柄等等)

 

4、完善login_page.py中的LoginPage类继承BasePage类,并完善代码

 

 

 完善login_page.py

 

 

 

 

 

 

 

 代码示例:

# encoding: utf-8
# @author: Jeffrey
# @file: login_page.py
# @time: 2022/7/2 12:39
# @desc: 页面是类  控件:属性  控件操作:方法
import os
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from common.base_page import BasePage
from common.log_utils import logger
from common.element_data_utils import ElementDataUtils

current_path = os.path.dirname(__file__)
driver_path = os.path.join(current_path, '../webdriver/chromedriver.exe')


class LoginPage(BasePage):

    def __init__(self,driver):
        # 调用父类的初始化
        super().__init__(driver)


        # 控件:属性
        # self.username_inputbox = self.driver.find_element(By.XPATH,'//input[@name="account"]')
        # self.password_inputbox = self.driver.find_element(By.XPATH,'//input[@name="password"]')
        # self.keep_login_box = self.driver.find_element(By.XPATH,'//input[@id="keepLoginon"]')
        # self.login_button = self.driver.find_element(By.XPATH,'//button[@id="submit"]')

        # 用户名输入框
        self.username_inputbox = {
     'element_name':'用户名输入框',
                                  'locator_type':'xpath',
                                  'locator_value':'//input[@name="account"]',
                                  'timeout':5}
        # 密码输入框
        self.password_inputbox = {
     'element_name':'密码输入框',
                                  'locator_type':'xpath',
                                  'locator_value':'//input[@name="password"]',
                                  'timeout':5}
        # 保持登录
        self.keep_login_box = {
     'element_name':'保持登录按钮',
                                  'locator_type':'xpath',
                                  'locator_value':'//input[@id="keepLoginon"]',
                                  'timeout':5}

        # 登录按钮
        self.login_button = {
     'element_name':'登录按钮',
                                  'locator_type':'xpath',
                                  'locator_value':'//button[@id="submit"]',
                                  'timeout':5}


    # 输入用户名
    def input_username(self,username):
        self.input(self.username_inputbox, username)

    # 输入密码
    def input_password(self,password):
        self.input(self.password_inputbox, password)

    # 点击保持登录
    def keep_login(self):
        self.click(self.keep_login_box)

    # 点击登录按钮
    def click_login(self):
        self.click(self.login_button)



if __name__ == '__main__':
    chrome_driver_path = Service(driver_path)
    driver = webdriver.Chrome(service=chrome_driver_path)
    loginpage = LoginPage(driver)
    loginpage.open_url('http://47.107.178.45/zentao/www/index.php?m=user&f=login')
    loginpage.set_browser_max()
    loginpage.implicitly_wait(30)
    loginpage.input_username('test01')
    loginpage.input_password('newdream123')
    loginpage.click_login()
小结:
在页面类拿到元素的识别信息字典-->通过BasePage的元素识别方法获取一个元素-->再操作元素--->再页面类再根据具体的业务封装操作

框架03--元素信息分离设计

思路:元素识别信息数据放代码中是否好维护?

数据的类型:1. 元素识别数据  2. 配置数据(url,路径,邮箱) 3. 测试用例数据

数据与代码的分离:

1.    数据放ini配件文件中

2.    放excel表中

3.    放 mysql

 

4.    放yaml文件中

数据放Excel表中思路

1. 新建一个element_info_datas文件夹,把元素识别的数据放Excel表中

2. 步骤2:编写测试代码,读取excel表中数据元素识别数据

3. 把读取excel的代码封装element_data_utils.py文件中

 

4. 步骤4:改造LoginPage,从Excel中拿元素识别数据

1.新建一个element_info_datas文件夹,把元素识别的数据放Excel表中

 

 

 

 

 

 数据结构:一个字典套一层字典:{ {},{},{}}

{

‘username_inputbox’:{
   'element_name':'用户名输入框',
'locator_type':'xpath','
locator_value':'//input[@name="account"]',
'timeout':5}

}

 

2、编写测试代码,读取excel表中数据元素识别数据

 

 

 安装一个1.2.0版本的xlrd    pip install xlrd==1.2.0

在samples文件下在demo中编写线性代码

 

 

 

3、把读取excel的代码封装element_data_utils.py文件中

 

 

 

代码示例:

# encoding: utf-8
# @author: Jeffrey
# @file: element_data_utils.py
# @time: 2022/7/2 18:40
# @desc: 读取excel工具类
import os
import xlrd


current_paht = os.path.dirname(__file__)
excel_path =os.path.join(current_paht, '../element_info_datas/element_info_datas.xlsx')


class ElementDataUtils(object):

    def __init__(self,page_name,excel_path = excel_path):
        self.excel_path = excel_path
        self.workbook = xlrd.open_workbook(self.excel_path)
        self.sheet = self.workbook.sheet_by_name(page_name)
        self.row_count = self.sheet.nrows


    def get_element_info(self):

        element_infos = {}
        for i in range(1,self.row_count):
            element_info = {}
            element_info['element_name'] = self.sheet.cell_value(i, 1)
            element_info['locator_type'] = self.sheet.cell_value(i, 2)
            element_info['locator_value'] = self.sheet.cell_value(i, 3)
            element_info['timeout'] = self.sheet.cell_value(i, 4)
            element_infos[self.sheet.cell_value(i, 0)] = element_info
        return element_infos

if __name__ == '__main__':
    elements = ElementDataUtils('login_page').get_element_info()
    print(elements)

 

4、改造LoginPage,从Excel中拿元素识别数据

 

 

 

代码示例:

# encoding: utf-8
# @author: Jeffrey
# @file: login_page.py
# @time: 2022/7/2 12:39
# @desc: 页面是类  控件:属性  控件操作:方法
import os
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from common.base_page import BasePage
from c
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值