web自动化——web数据驱动、夹具的处理和日志的封装

  • UI测试中,页面类的类名,建议使用驼峰命名
  • 请不要写拼音,不同的页面可使用翻译软件翻译之后进行命名
  • py文件的命名需要小写,如果有多个单词,使用下划线隔开
  • 测试用例使用test文件和函数开头,方便pytest识别
  • 元素的命名,一定要有自己的规范,建议使用locator作为后缀,或者使用oc简写

web测试的数据驱动

数据驱动

  • 参数化:因为数据数据的不同,从而产生不同的人结果
    • 简单的来说就是将输入的数据作为一个变量传入
    • 例如搜索商品,不同的分搜索关键字和搜索条件作为入惨,会得到不同的结果
  • 数据驱动:测试数据的改变驱动自动化测试的执行,产生不同的测试结果
    • 数据驱动本质上是高级的参数化
  • 对于测试数据,我们可以将其放在代码的数据结构中(例如列表),也可以将其放在外部文件中 (例如json,excel,yaml文件)或者数据库中,结合pytest中的装饰器去实现

数据驱动的实现方式

  • 结合pytest的驱动装饰器,
  • 方式:@pytest.mark.parametrize("参数名",列表数据)
  • 详细用法详见文章
  • 代码示例:
import pytest
datas = [("tom",1234),("leo",2345),("rose",3456)]

@pytest.mark.parametrize('username,password',datas)
def test_one(username,password):
    print(username,password)

数据驱动和web测试的结合

  • 什么时候适合做UI自动化的驱动测试
    • 1.操作流程一致
    • 2.断言方式一样
    • 这里的断言方式指的是:其使用同一个元素进行断言
    • 如果同一个元素,因为不同的场景产生的提示信息不同,可以将其参数化,通过列表进行传递
  • 应用场景比较受限,常用于登录、搜索的场景
  • 测试用例的代码如下:
import pytest

from PageObject.home_page import HomePage
from PageObject.login_pages import LoginPage
import time

cases_data = [
    {"name":"","pwd":"lemon123456"},
    {"name":"lemon_auto1111111","pwd":"lemon123456"},
    {"name":"lem","pwd":"lemon123456"},
]

@pytest.mark.parametrize("datas",cases_data)
def test_login_input_error(setUp_tearDown,datas):
    home = HomePage(setUp_tearDown)
    home.click_login()  # 点击登录按钮
    LoginPage(setUp_tearDown).login_operate(datas.get("name"),datas.get("pwd"))  # 登录操作
    time.sleep(2)
    try:
        assert LoginPage(setUp_tearDown).get_user_input_error_tips()
        print("测试通过")
    except Exception:
        print("测试未通过")
  • 登录界面和home界面的代码如下:
# 基础页
from selenium.webdriver import ActionChains
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
import os

class BasicOperate:

    def __init__(self,driver):
        #初始化函数,实例化(创建对象的时候)会先执行init方法
        #可以将driver驱动作为传递进来
        self.driver = driver

    # 等待元素可见
    def wait_located(self, ele):
        try:
            return WebDriverWait(self.driver, 20).until(EC.visibility_of_element_located(ele))
        except Exception:
            print("等待超时")
    # 等待元素可点击
    def wait_click(self, ele):
        try:
            return WebDriverWait(self.driver, 20).until(EC.element_to_be_clickable(ele))
        except Exception:
            print("等待超时")


    # 等待元素存在
    def wait_presence(self, ele):
        try:
            return WebDriverWait(self.driver, 20).until(EC.presence_of_element_located(ele))
        except Exception:
            print("等待超时")
            
# 首页
from Common.basic_pages import BasicOperate
from selenium.webdriver.common.by import By

class HomePage(BasicOperate):

    # 首页登录按钮
    login_button_locator = (By.XPATH, '//a[text()="登录"]')

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

#登录界面
from selenium.webdriver.common.by import By
from Common.basic_pages import BasicOperate

class LoginPage(BasicOperate):
    # 登录输入框
    login_box_locator = (By.XPATH, '//div[@class="login-con"]//input[contains(@placeholder,"手机号")]')
    # 密码输入框
    pwd_box_locator = (By.XPATH, '//div[@class="login-con"]//input[contains(@placeholder,"密码")]')
    # 登录按钮
    login_button_locator = (By.XPATH, '//div[@class="login-con"]//a[text()="登录"]')
    # 用户名输入框的错误提示文本
    user_input_error_tips_locator = (By.XPATH,'//div[@class="login-con"]/div[2]')


    def login_operate(self,phone,pwd):
        self.wait_located(self.login_box_locator).send_keys(phone)#输入手机号码
        self.wait_located(self.pwd_box_locator).send_keys(pwd)#输入密码
        self.wait_click(self.login_button_locator).click()#点击登录

    # 账号不输入提示
    def get_user_input_error_tips(self):
        return self.wait_located(self.user_input_error_tips_locator).text
  • conftest的代码设置如下:
from selenium import webdriver
import pytest

@pytest.fixture(scope="class") #声明这是一个夹具
def setUp_tearDown():
    print("======用例开始执行=========")
    driver = webdriver.Chrome()
    driver.get("XXX")
    driver.maximize_window()
    yield driver
    driver.quit()

web自动化之夹具的处理

  • 夹具可以放在fonftest.py文件中,最好和测试用例在同一个层级
  • 具体用法,详见pytest基础
  • 首先,可以将打开浏览器,进入网站,将浏览器最大化放在前置条件中
  • 将浏览器推出放在后置条件中
  • 中间使用yield返回driver
  • 然后将driver作为参数传递至测试用例中
  • 这些上面的代码已经体现
  • 运行UI自动化的时候,不建议所有的测试用例只打开/关闭一次浏览器,因为UI自动化不稳定
    • 如果在运行某条用例失败了,后面的测试用例是从失败的页面执行的,导致后面的用例接连十倍,因为可能找不到对应的元素
    • 建议每条用例都可以单独的打开/关闭浏览器

如何将登录放在夹具中

  • 可以在conftest中新增一个login的家具
  • 家具中可以将打开浏览器的家具作为参数传入调用,中间使用yield返回打来浏览器家具的返回值
  • 测试用例中,直接使用登录的夹具,driver使用登录的返回值代替
  • 下面conftest代码中,后置先执行的是login的后置(如果有的话),然后才回去执行被调用者的后置
  • 夹具的代码如下:
from selenium import webdriver
import pytest
import time
from PageObject.home_page import HomePage
from PageObject.login_pages import LoginPage


@pytest.fixture() #声明这是一个夹具
def setUp_tearDown():
    print("======测试前置=========")
    driver = webdriver.Chrome()
    driver.get("XXX")
    driver.maximize_window()
    yield driver
    print("======测试后置=========")
    driver.quit()

@pytest.fixture()
def login(setUp_tearDown):
    home = HomePage(setUp_tearDown)
    home.click_login()  # 点击登录按钮
    LoginPage(setUp_tearDown).login_operate("lemon_auto", "lemon123456")  # 登录操作
    time.sleep(2)
    yield setUp_tearDown
  • 测试用例的代码如下
from PageObject.user_pages import UserPage
from PageObject.address_pages import AddressPage
import time

def test_add_address(login):
    UserPage(login).user_center_click()#进入个人中心,点击左侧新增地址
    AddressPage(login).add_address_operate("依依","18000000001","河南省","郑州市","巩义市","小关镇口头村")#输入信息,选择区域
    #下面写断言
  • 其他模块的代码可以不变化(基础类,其他页面操作或者元素定位方式等)

日志的封装

  • 通过loguru第三方库,在自动化执行的过程中记录操作的轨迹,方便在测试完成之后,追溯到及定位问题
  • loguru的用法/logging的用法
  • 日志记录有info,error,debug,warn,critical(崩溃)
  • 直接在代码中进行添加
  • 如果要进行跟踪,建议将普通的点击,输入信息,获取文本等操作进行二次封装
  • 具体代码——基础页面
from selenium.webdriver import ActionChains
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
from loguru import logger
import os

class BasicOperate:

    def __init__(self,driver):
        #初始化函数,实例化(创建对象的时候)会先执行init方法
        #可以将driver驱动作为传递进来
        self.driver = driver


    # 等待元素可见
    def wait_located(self, ele):
        try:
            return WebDriverWait(self.driver, 20).until(EC.visibility_of_element_located(ele))
        except Exception as e:
            logger.error(f"等待元素可见发生异常:{e}")
            raise
            #如果元素没有找到,不在这里抛出异常的话,会显示通过的
    # 等待元素可点击
    def wait_click(self, ele):
        try:
            return WebDriverWait(self.driver, 20).until(EC.element_to_be_clickable(ele))
        except Exception as e :
            logger.error(f"等待元素可被点击发生异常:{e}")
            raise


    # 等待元素存在
    def wait_presence(self, ele):
        try:
            return WebDriverWait(self.driver, 20).until(EC.presence_of_element_located(ele))
        except Exception as e:
            logger.error(f"等待元素存在发生异常:{e}")
            raise

    # 滚动条
    def scroll_operate(self,ele_locator,text):
        '''
        :param ele_locator:元素定位的元组
        样式:(By.XPATH, '//a[text()="登录"]')
        :param text: 判断代码在HTML中的元素
        :return:
        '''
        try:
            while True:
                if text in self.driver.page_source:
                    break
                ele = self.wait_located(ele_locator)
                self.driver.execute_script("arguments[0].scrollIntoView(true)", ele)
                time.sleep(1)
        except Exception as e:
            logger.error(f"滚动条操作发生异常:{e}")

    #javascript元素点击
    def js_click(self,ele_locator):
        '''
        :param ele_locator: ele_locator:元素定位的元组
        样式:(By.XPATH, '//a[text()="登录"]')
        :return:
        '''
        try:
            ele = self.wait_located(ele_locator)
            self.driver.execute_script("arguments[0].click()",ele)
        except Exception as e:
            logger.error(f"js_click发生异常:{e}")

    #去除元素的属性
    def remove_ele_att(self,ele_locator,att_name):
        '''
        :param ele_locator: ele_locator: ele_locator:元素定位的元组
        样式:(By.XPATH, '//a[text()="登录"]')
        :param att_name: 要去除的属性的名称
        :return:
        '''
        try:
            ele = self.wait_located(ele_locator)
            self.driver.execute_script(f"arguments[0].removeAttribute({att_name})",ele)
        except Exception as e:
            logger.error(f"js_去除元素属性发生异常:{e}")

    #鼠标点击
    def mouse_click(self,ele_locator):
        '''
        :param ele_locator: ele_locator: ele_locator:元素定位的元组
        样式:(By.XPATH, '//a[text()="登录"]')
        :return:
        '''
        try:
            ele = self.wait_located(ele_locator)
            ActionChains(self.driver).click(ele).perform()
        except Exception as e:
            print(f"鼠标点击发生错误:{e}")

    #窗口切换

    def switch_win(self,text):
        '''
        :param text: 判断是否切换窗口,一般是窗口的title
        :return:
        '''
        try:
            logger.info(f"窗口切换到{text}")
            handles = self.driver.handles
            for handle in handles:
                if self.driver.title == text:
                    break
                self.driver.switch_to_window(handle)
        except Exception as e:
            logger.error(f"切换窗口失败:{e}")

    def upload_file(self,file_path,exe_path):
        '''
        :param file_path: 上传文件的路径
        :return:
        '''
        try:
            os.system(f'{exe_path} {file_path}')
        except Exception as e:
            print(f"上传文件失败:{e}")

    def p_click(self,ele):
        try:
            logger.info(f"点击元素:{ele}")
            self.wait_click(ele).click()
        except Exception as e:
            logger.error(f"点击元素出现异常:{e}")

    def input_text(self,ele,data):
        try:
            logger.info(f"向输入框{ele}中输入数据{data}")
            self.wait_located(ele).send_keys(data)
        except Exception as e:
            logger.error(f"向输入框中输入信息异常:{e}")

    #获取元素文本
    def get_ele_text(self,ele):
        logger.info(f"获取{ele}的文本")
        return self.wait_located(ele).text

    def get_ele_is_display(self,ele):
        logger.info(f"判断{ele}是否存在")
        return self.wait_located(ele).is_displayed()
  • conftest夹具文件
from selenium import webdriver
import pytest
import time
from PageObject.home_page import HomePage
from PageObject.login_pages import LoginPage


@pytest.fixture() #声明这是一个夹具
def setUp_tearDown():
    print("======测试前置=========")
    driver = webdriver.Chrome()
    driver.get("XXXX")
    driver.maximize_window()
    yield driver
    print("======测试后置=========")
    driver.quit()

@pytest.fixture()
def login(setUp_tearDown):
    home = HomePage(setUp_tearDown)
    home.click_login()  # 点击登录按钮
    LoginPage(setUp_tearDown).login_operate("lemon_auto", "lemon123456")  # 登录操作
    time.sleep(2)
    yield setUp_tearDown
  • 页面类,写其中一个——获取地址
import time

from Common.basic_pages import BasicOperate
from selenium.webdriver.common.by import By

class AddressPage(BasicOperate):


    # 新增收货地址
    add_address_locator = (By.XPATH, '//a[@class="add-btn"]')
    # 收件人输入框
    recipient_input_locator = (By.XPATH, '//div[text()="收件人:"]/following-sibling::div/input')
    # 手机号码输入框
    phone_input_locator = (By.XPATH, '//div[text()="手机号码:"]/following-sibling::div/input')
    # 省选择
    input_province_locator = (By.XPATH, '//div[@prop="province"]//input')
    # 选项北河南省
    li_peovince_locator = (By.XPATH, f'//li[text()="河南省"]')
    # 市选择
    input_city_locator = (By.XPATH, '//div[@prop="city"]//input')
    # 选择直辖市
    li_city_locator = (By.XPATH, '//li[text()="郑州市"]')
    # 区域选择
    input_area_locator = (By.XPATH, '//div[@prop="area"]//input')
    # 巩义市
    li_area_locator = (By.XPATH, '//li[text()="巩义市"]')
    # 详细地址
    detail_address_locator = (By.XPATH, '//div[text()="详细地址:"]/following-sibling::div/input')
    # 保存收件人信息
    save_msg_locator = (By.XPATH, '//a[text()="保存收件人信息"]')
    # # 滚动条
    # soll_peovince_locator = (By.XPATH,
    #         '//li[text()="北京市"]/ancestor::div[contains(@class,"el-select-dropdown__wrap")]/following-sibling::div[contains(@class,"el-scrollbar__bar is-vertical")]')
    # soll_city_locator = (By.XPATH,
    #         '//li[text()="郑州市"]/ancestor::div[contains(@class,"el-select-dropdown__wrap")]/following-sibling::div[contains(@class,"el-scrollbar__bar is-vertical")]')
    # soll_area_locator = (By.XPATH,
    #         '//li[text()="巩义市"]/ancestor::div[contains(@class,"el-select-dropdown__wrap")]/following-sibling::div[contains(@class,"el-scrollbar__bar is-vertical")]')


    def add_address_operate(self,name,phone,peovince,city,area,msg):
        self.p_click(self.add_address_locator)#点击收货地址
        self.input_text(self.recipient_input_locator,name)#输入用户名
        self.input_text(self.phone_input_locator,phone)# 输入手机号

        self.p_click(self.input_province_locator)# 点击省选择框

        self.scroll_operate(self.li_peovince_locator,peovince)#等待省份滑动至可见区域
        time.sleep(1)
        self.p_click(self.li_peovince_locator)#点击河南省

        self.p_click(self.input_city_locator)# 点击城市选择框
        time.sleep(2)
        self.scroll_operate(self.li_city_locator,city)#等待郑州市滑动至可见区域
        time.sleep(1)
        self.p_click(self.li_city_locator)#点击郑州市

        self.p_click(self.input_area_locator)# 点击区域选择框
        time.sleep(2)
        self.scroll_operate(self.li_area_locator,area)#等待巩义市滑动至可见区域
        time.sleep(1)
        self.p_click(self.li_area_locator)#点击巩义市

        self.input_text(self.detail_address_locator,msg)
        # self.p_click(self.save_msg_locator)
        time.sleep(3)
  • 测试用例,写其中一个,获取地址
from PageObject.user_pages import UserPage
from PageObject.address_pages import AddressPage


def test_add_address(login):
    UserPage(login).user_center_click()#进入个人中心,点击左侧新增地址
    AddressPage(login).add_address_operate("依依","18000000001","河南省","郑州市","巩义市","小关镇口头村")#输入信息,选择区域
    #下面写断言
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值