UI自动化测试示例:python+pytest+selenium+allure

重点应用是封装、参数化:

比如在lib文件夹下,要存储封装好的方法和必要的环境变量(指网址等)

1.cfg.py:封装网址和对应的页面

python 复制代码
SMP_ADDRESS = 'http://127.0.0.1:8234'

SMP_URL_LOGIN        = f'{SMP_ADDRESS}/login.html'
SMP_URL_DEVICE_MODEL = f'{SMP_ADDRESS}/index.html#/devicemodel'
SMP_URL_SERVICE_RULE = f'{SMP_ADDRESS}/index.html#/svcrule'

二、登录部分

2.1 lib.py-登录模块的封装

主要就是输入账号名和密码进行测试。

python 复制代码
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import Select
import time
from lib.cfg import *

class SMP_UI:
    def __init__(self):
        options = webdriver.ChromeOptions()
        #排除一些不需要的日志输出。
        options.add_experimental_option( 'excludeSwitches', ['enable-logging'])

        self.wd = webdriver.Chrome(options=options)
        self.wd.implicitly_wait(3) #隐式等待时间为5秒,等待元素加载完成

    #登录模块测试
    def login(self, username, password):
        self.wd.get(SMP_URL_LOGIN) #打开登录网址
        #time.sleep(1)#sleep方便观察
        if username is not None:
            self.wd.find_element(By.ID, 'username').send_keys(username)
        if password is not None:
            self.wd.find_element(By.ID, 'password').send_keys(password)
        self.wd.find_element(By.ID, 'loginBtn').click()#点击登录按钮

smpUI = SMP_UI()

2.2自动化测试脚本代码-登录

cpp 复制代码
from selenium.webdriver.common.by import By
import time,pytest
from lib.webUI_smp import smpUI

def test_SMP_login_001():#正确账号和密码
    smpUI.login('byhy','sdfsdf' )
    nav = smpUI.wd.find_elements(By.TAG_NAME, 'nav') #登陆后就可以查看nav了,有信息代表登陆成功
    assert nav != []

@pytest.fixture
def clearAlert():
    yield
    try:
        smpUI.wd.switch_to.alert.accept()
    except Exception as e:
        print(e)

@pytest.mark.parametrize('username, password, expectedalert', [
        (None, 'sdfsdf', '请输入用户名'),#空用户名
        ('byhy', None, '请输入密码'),#空密码
        ('byhy', 'sdfsd',   '登录失败: 用户名或者密码错误'),#少输入密码
        ('byhy', 'sdfsdff', '登录失败: 用户名或者密码错误'),#多输入密码
        ('byh', 'sdfsdf',   '登录失败: 用户名不存在'),#少输入用户名
        ('byhyy', 'sdfsdf', '登录失败: 用户名不存在'),#多输入用户名
    ])
def test_SMP_login_002(username, password, expectedalert, clearAlert):
    smpUI.login(username, password)
    smpUI.wd.implicitly_wait(5)#隐式等待

    alert = smpUI.wd.switch_to.alert.text
    assert alert == expectedalert #对弹出框进行断言对比

三、商品部分

3.1 lib.py-商品服务模块的封装

主要就是添加商品、删除商品等,输出第一页的第一条的值(检查返回值)

python 复制代码
    #添加设备模块检测
    def add_device_model(self, devType, name, desc):
        # 实例化选择框做为对象
        select = Select(smpUI.wd.find_element(By.ID, "device-type"))
        # 选择对应列表中的种类
        select.select_by_visible_text(devType)
        #清除填充框后填-设备型号
        ele = smpUI.wd.find_element(By.ID, 'device-model')
        ele.clear()
        ele.send_keys(name)
        #清除填充框后填-型号描述
        ele = smpUI.wd.find_element(By.ID, 'device-model-desc')
        ele.clear()
        ele.send_keys(desc)
        #点提交
        smpUI.wd.find_element(By.CSS_SELECTOR, '.add-one-submit-btn-div .btn').click()
        # time.sleep(1)

    #得到第一页的设备信息(注:一页五条),是显示第一页的第一个数据
    def get_first_page_device_models(self):
        time.sleep(1)

        self.wd.implicitly_wait(0)
        values = self.wd.find_elements(By.CSS_SELECTOR,'.field-value')
        self.wd.implicitly_wait(10)

        deviceModels = []
        for idx, value in enumerate(values):
            if (idx+1) % 3 == 0:
                deviceModels.append(
                    [values[idx-2].text, values[idx-1].text, values[idx].text])

        return deviceModels


    #删除第一个项目
    def del_first_item(self) -> bool:
        self.wd.implicitly_wait(0)
        delBtn = self.wd.find_elements(By.CSS_SELECTOR,'.result-list-item:first-child .result-list-item-btn-bar span:first-child')
        self.wd.implicitly_wait(10)
        if not delBtn:
            return False
        delBtn[0].click()
        self.wd.switch_to.alert.accept()
        return True

3.2自动化测试脚本代码-商品服务

之间登录商品增删改查的界面。注意编完数据后要删除

python 复制代码
from selenium.webdriver.common.by import By
import time,pytest
from lib.webUI_smp import smpUI
from selenium.webdriver.support.ui import Select
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from lib.cfg import *

@pytest.fixture(scope='module')
def inDeviceModelMgr():
    print('inDeviceModelMgr setup')
    smpUI.login('byhy','sdfsdf' ) #直接登录
    smpUI.wd.get(SMP_URL_DEVICE_MODEL)#登录后转到SMP_URL_DEVICE_MODEL这个主界面
    yield


@pytest.fixture()
def delAddedDeviceModel():
    yield
    print('删除添加的设备型号')
    smpUI.del_first_item()

@pytest.mark.parametrize('type, device, desc', [
        ("存储柜", 'hebut-cn-box-01', '河北工业大学菜鸟柜子01柜-10大20中40小'),#添加设备-存储柜测试
        ("存储柜", '河'*100, '河北工业大学菜鸟柜子02柜-10大20中40小'),#测试名称最大长度
("电瓶车充电站", 'hebut-dpccdz-es-01', '河北工业大学北辰充电站01站-220v电瓶车充电站'),#添加设备-电瓶车充电站
("洗车站", 'hebut-xcz-washcar-01', '河北工业大学洗车站东站'),#添加设备-洗车站测试
("汽车充电站", 'hebut-powercar-01', '河北工业大学7千瓦汽车充电站01'),#添加设备-汽车充电站
    ])
#添加设备-存储柜测试
def test_SMP_device_model_001(type,device,desc,inDeviceModelMgr, delAddedDeviceModel):
    # 点击添加按钮
    topBtn = smpUI.wd.find_element(By.CSS_SELECTOR, '.add-one-area > span')
    if topBtn.text == '添加':
        topBtn.click()
    smpUI.add_device_model(type,device,desc)
    dms = smpUI.get_first_page_device_models()
    assert  dms == [[type,device,desc    ]]
    smpUI.wd.implicitly_wait(3)



def test_SMP_device_model_501(inDeviceModelMgr, delAddedDeviceModel):
    # 点击添加按钮
    topBtn = smpUI.wd.find_element(By.CSS_SELECTOR, '.add-one-area > span')
    if topBtn.text == '添加':
        topBtn.click()
    smpUI.add_device_model("存储柜", 'daixiugai-01', '待修改测试数据-01')
    smpUI.wd.implicitly_wait(3)

    # 点击修改按钮
    changeBtn = smpUI.wd.find_element(By.XPATH,'/html/body/main/div[3]/div/div[2]/span[2]').click()
    # if changeBtn.text == '修改':
    #     changeBtn.click()
        # 实例化选择框做为对象
    select = Select(smpUI.wd.find_element(By.XPATH,'/html/body/main/div[3]/div/div[1]/div[1]/select'))
        # 选择对应列表中的种类
    select.select_by_visible_text("洗车站")
        # 清除填充框后填-设备型号
    ele = smpUI.wd.find_element(By.XPATH,'/html/body/main/div[3]/div/div[1]/div[2]/input')
    ele.clear()
    ele.send_keys('修改后的型号1')

        # 清除填充框后填-型号描述
    ele = smpUI.wd.find_element(By.XPATH,'/html/body/main/div[3]/div/div[1]/div[3]/input')
    ele.clear()
    ele.send_keys('修改后的型号描述')

        # 点提交
    smpUI.wd.find_element(By.XPATH,'/html/body/main/div[3]/div/div[2]/span[1]').click()
    dms = smpUI.get_first_page_device_models()
    assert  dms == [["洗车站",'修改后的型号1','修改后的型号描述' ]]
    smpUI.wd.implicitly_wait(3)



def test_SMP_device_model_601(inDeviceModelMgr, delAddedDeviceModel):
    # 点击添加按钮
    topBtn = smpUI.wd.find_element(By.CSS_SELECTOR, '.add-one-area > span')
    if topBtn.text == '添加':
        topBtn.click()
    smpUI.add_device_model("存储柜", 'daixshanchu-01', '待删除测试数据-01')

    time.sleep(1)

    smpUI.del_first_item()

    dms = smpUI.get_first_page_device_models()
    assert ['存储柜', 'daixshanchu-01', '待删除测试数据-01'] not in dms
    smpUI.wd.implicitly_wait(3)

四、业务规则部分

4.1 lib.py-业务规则模块的封装

python 复制代码
    def add_svc_rule(self, ruleName:str, ruleType:str,minFee:str,
                     estFee:str, feeRate=None, desc:str=None):
        """
        添加业务规则
        :param ruleName: 规则名称
        :param ruleType: 规则类型,只能是:后付费-上报业务量、预付费-下发费用、预付费-下发业务量
        :param minFee: 最小费用,不需要时,填写空字符串
        :param estFee: 预计费用,不需要时,填写空字符串
        :param desc: 描述
        :param feeRate: 费率, 如果ruleType是
           预付费-下发费用: 不用填写
           预付费-下发业务量: 格式为 ['千瓦时', '1'], 元素分别是 单位、单价
           后付费-上报业务量: 格式为 [
                ['10L', '小时','1'],
                ['20L', '小时','2'],
            ], 每个元素里面分别是 : 业务码、单位、单价
        :return:
        """
        #名称输入
        ele = self.wd.find_element(By.CSS_SELECTOR, '.add-one-form > .field:nth-child(1) >input')
        ele.clear()
        ele.send_keys(ruleName)

        select = Select(self.wd.find_element(By.CSS_SELECTOR, ".add-one-form select"))
        select.select_by_visible_text(ruleType)

        if ruleType != '后付费-上报业务量': #预付费-下发业务量操作
            ele = self.wd.find_element(By.CSS_SELECTOR, '.add-one-form > .field:nth-child(3) >input')
            ele.clear()
            if minFee:
                ele.send_keys(minFee)#输入最小消费

            ele =  self.wd.find_element(By.CSS_SELECTOR, '.add-one-form > .field:nth-child(4) >input')
            ele.clear()
            if estFee:
                ele.send_keys(estFee)#输入预估消费

        # 三者都有描述, 用xpath而不用 .field:nth-child 因为后付费-上报业务量 次序会变
        if desc:
            ele =  self.wd.find_element(By.XPATH,"//*[@class='add-one-submit-btn-div']/preceding-sibling::*[1]/input")
            ele.clear()
            ele.send_keys(desc)#输入对应描述

        # 费率填写
        if ruleType == '预付费-下发费用':
            # 没有费率设置
            pass
        elif ruleType == '后付费-上报业务量':
            # 先删除上次添加遗留的费率
            self.wd.implicitly_wait(3)  # 先修改短等待时间
            while True:
                eles = self.wd.find_elements(By.CSS_SELECTOR, '.fee-rate span:last-child')
                if eles:
                    eles[0].click() #删除之前的费率
                    time.sleep(0.5)
                else:
                    break

            self.wd.implicitly_wait(3)  # 再改回来

            for one in feeRate:#遍历每个费率块
                self.wd.find_element(By.CSS_SELECTOR,'.fee-rate-list button').click()
                #锚定整个框
                entry = self.wd.find_element(By.CSS_SELECTOR,'div.fee-rate:nth-last-child(2)')

                # 输入业务码
                entry.find_element(By.CSS_SELECTOR, 'input:nth-of-type(1)').send_keys(one[0])
                # 输入计费单位
                entry.find_element(By.CSS_SELECTOR, 'input:nth-of-type(2)').send_keys(one[1])
                # 输入单位价格
                entry.find_element(By.CSS_SELECTOR, 'input:nth-of-type(3)').send_keys(one[2])

        elif ruleType == '预付费-下发业务量':
            #锚定整个框
            entry = self.wd.find_element(By.CSS_SELECTOR, 'div.fee-rate')
            # 输入计费单位
            entry.find_element(By.CSS_SELECTOR, 'input:nth-of-type(1)').send_keys(feeRate[0])
            # 输入单位价格
            entry.find_element(By.CSS_SELECTOR, 'input:nth-of-type(2)').send_keys(feeRate[1])
        else:
            raise Exception('ruleType 参数值错误')

        # 确定提交
        self.wd.find_element(By.CSS_SELECTOR, '.add-one-submit-btn-div .btn').click()
        self.wd.implicitly_wait(3)



    #业务部分查询首页
    def get_first_page_svc_rules(self):
        time.sleep(1)

        self.wd.implicitly_wait(0)
        items = self.wd.find_elements(By.CSS_SELECTOR,'.result-list-item-info')
        self.wd.implicitly_wait(10)

        rules = []
        for item in items:
            nameValueList = item.find_elements(By.CSS_SELECTOR, '.field>.field-name, .field>.field-value')
            itemInfo = []
            for idx, _ in enumerate(nameValueList):
                if (idx+1) % 2 == 0:
                    nameEle,valueEle = nameValueList[idx-1], nameValueList[idx]
                    if nameEle.text == '规则内容':
                        ruleContent = {}
                        sfns = valueEle.find_elements(By.CSS_SELECTOR,'.sub-field-name')
                        for sfnEle in sfns:
                            sfn = sfnEle.text
                            sfvEle = sfnEle.find_element(By.XPATH, "following-sibling::*[1]")
                            if sfn == '费率':
                                ruleContent[sfn] = sfvEle.text
                            else:
                                ruleContent[sfn] = sfvEle.text

                        itemInfo.append(ruleContent)
                    else:
                        itemInfo.append(valueEle.text)

            rules.append(itemInfo)

        return rules

    #删除第一个项目
    def del_first_item(self) -> bool:
        self.wd.implicitly_wait(0)
        delBtn = self.wd.find_elements(By.CSS_SELECTOR,'.result-list-item:first-child .result-list-item-btn-bar span:first-child')
        self.wd.implicitly_wait(10)
        if not delBtn:
            return False
        delBtn[0].click()
        self.wd.switch_to.alert.accept()
        return True

4.2自动化测试脚本代码-业务规则

注意事项跟3.2的商品的自动化测试脚本类似

python 复制代码
from selenium.webdriver.common.by import By
import time,pytest
from lib.webUI_smp import smpUI
from lib.cfg import *

@pytest.fixture(scope='module')
def inServiceRuleMgr():
    print('** inServiceRuleMgr setup **')
    smpUI.login('byhy','sdfsdf' )
    smpUI.wd.get(SMP_URL_SERVICE_RULE)
    yield


@pytest.fixture()
def delAddedServiceRule():
    yield
    print('** 删除添加的业务规则')
    smpUI.del_first_item()

@pytest.mark.parametrize('svcname, svctype,paymin,pay, svcdesc,jifeidanwei,feilv', [
        ("全国-电瓶车充电费率1", "预付费-下发业务量", "0.1","2","全国-电瓶车充电费率1的描述",
        ['千瓦时', '1'],None
        ),#添加预付费-下发业务量

    ])
def test_SMP_service_rule_001(svcname, svctype,paymin,pay, svcdesc,jifeidanwei,feilv,inServiceRuleMgr, delAddedServiceRule):
    # 点击添加按钮
    topBtn = smpUI.wd.find_element(By.CSS_SELECTOR,'.add-one-area > span')
    if topBtn.text == '添加':
        topBtn.click()

    smpUI.add_svc_rule(
        svcname, svctype,paymin,pay, jifeidanwei,svcdesc)

    dms = smpUI.get_first_page_svc_rules()
    assert dms[0][:3] == [svcname, svctype, {'最小消费': paymin, '预估消费': pay, '费率': f'单位:{jifeidanwei[0]} \n单价:{jifeidanwei[1]}'}]


@pytest.mark.parametrize('svcname, svctype,paymin,pay, svcdesc,jifeidanwei,feilv', [
        ("南京-洗车机费率1", "预付费-下发费用", "2","10","南京-洗车机费率1的描述",
        None,None
        ),#预付费-下发费用

    ])
def test_SMP_service_rule_101(svcname, svctype,paymin,pay, svcdesc,jifeidanwei,feilv,inServiceRuleMgr, delAddedServiceRule):
    # 点击添加按钮
    topBtn = smpUI.wd.find_element(By.CSS_SELECTOR,'.add-one-area > span')
    if topBtn.text == '添加':
        topBtn.click()

    smpUI.add_svc_rule(
        svcname, svctype,paymin,pay,svcdesc)

    dms = smpUI.get_first_page_svc_rules()
    assert dms[0][:3] == [svcname, svctype, {'最小消费': paymin, '预估消费': pay}]


@pytest.mark.parametrize('svcname, svctype,paymin,pay, svcdesc,jifeidanwei,feilv', [
        ("南京-存储柜费率1", "预付费-下发业务量", None,None,None,
        None,[['100L','小时','2']]
        ),#添加预付费-下发业务量,['50L','小时','1'],['10L','小时','0.5']

    ])
def test_SMP_service_rule_201(svcname, svctype,paymin,pay, svcdesc,
                              jifeidanwei,feilv,inServiceRuleMgr, delAddedServiceRule):
    # 点击添加按钮
    topBtn = smpUI.wd.find_element(By.CSS_SELECTOR,'.add-one-area > span')
    if topBtn.text == '添加':
        topBtn.click()

    smpUI.add_svc_rule(svcname, svctype, paymin, pay,  svcdesc, feilv)

    dms = smpUI.get_first_page_svc_rules()
    assert dms[0][:3] == [svcname, svctype, {'业务码': f'单位:{feilv[0]} \n计费单位:{feilv[1]} \n单位价格:{feilv[2]}'}]

还有部分没粘上去,万变不离其宗,还要接口自动化测试,这里先不写,下次再写。

相关推荐
try2find13 分钟前
安装llama-cpp-python踩坑记
开发语言·python·llama
博观而约取1 小时前
Django ORM 1. 创建模型(Model)
数据库·python·django
精灵vector3 小时前
构建专家级SQL Agent交互
python·aigc·ai编程
q567315233 小时前
Java Selenium反爬虫技术方案
java·爬虫·selenium
Zonda要好好学习3 小时前
Python入门Day2
开发语言·python
Vertira3 小时前
pdf 合并 python实现(已解决)
前端·python·pdf
太凉3 小时前
Python之 sorted() 函数的基本语法
python
项目題供诗3 小时前
黑马python(二十四)
开发语言·python
晓13134 小时前
OpenCV篇——项目(二)OCR文档扫描
人工智能·python·opencv·pycharm·ocr
是小王同学啊~4 小时前
(LangChain)RAG系统链路向量检索器之Retrievers(五)
python·算法·langchain