UI自动化测试:流程测试的demo

测试场景:用UI自动化测试一个流程:登录———加入购物车场景
以小米商城为例,测试流程:
首页(定位登录)-登录(输入用户和密码)- 首页(查询)- 商品页(定位第一个商品)-商品详情页(定位购物车)-购物车页面(认购)-购物车页面
1、common层
在这里插入图片描述
deal_log文件:

import logging
from datetime import datetime
from config.config import *

class Logs:
    # logging模块默认设置的日志级别是warning,而debug和info的级别是低于warning的,所以不会打印这两种日志信息
    def __init__(self, logger=None):
        # 1.定义日志收集器
        # 创建日志器logger对象以及日志级别
        self.logger = logging.getLogger(logger)  # 初始化日志类,定义一个日志收集器
        self.logger.setLevel(logging.DEBUG)  # 设置收集器的级别,不设定的话,默认收集warning及以上级别的日志
        # self.logger.setLevel("DEBUG")  # 也可以设置日志级别

        # self.logTime = datetime.now().strftime("%Y_%m_%d_%H_%M")
        self.logTime = datetime.now().strftime("%Y_%m_%d")
        self.logPath = LogPath
        self.logName = self.logPath + self.logTime + '.log'
        # self.logName = r'../logs\{}.log'.format(time.strftime('%Y-%m-%d-%H:%M:%S', time.localtime()))  # r是防止字符转转义,保留原有的样子(注意:返回上一次的路径是两个..)

        # 创建处理器handler以及日志级别
        # conlHandler = logging.StreamHandler()  # 此处理器是输出到控制台
        fileHandler = logging.FileHandler(self.logName, 'a', encoding='utf-8')  # 此处理器是输出到文件
        # conlHandler.setLevel('DEBUG')  # 设置控制台输出日志级别ERROR 或DEBUG
        fileHandler.setLevel('INFO')  # 设置文件输出日志级别  ERROR'

        # 设置日志输出格式
        # %(asctime)s:打印日志的时间, %(filename)s:打印当前执行程序名, %(levelname)s:打印日志级别名称,
        # %(lineno)s:打印日志的当前行号, %(message)s:打印日志信息, %(name)s:Logger的名字
        # conlFormatter = logging.Formatter(
        #     "%(asctime)s-%(lineno)d-[%(levelname)s]-[msg]: %(message)s")  # 输出到控制台的格式
        fileFormatter = logging.Formatter(
            "%(asctime)s-%(name)s-%(lineno)d-[%(levelname)s]-[msg]: %(message)s")  # 输出到文件中的格式

        # 用setFormatter()将上面设置的formatter配置到handler中
       #  conlHandler.setFormatter(conlFormatter)  # 配置控制台日志输出格式
        fileHandler.setFormatter(fileFormatter)  # 配置文件日志输出格式

        # 用addHandler()将配置好格式的Haddler添加到logger中,进行过滤
        # self.logger.addHandler(conlHandler)
        self.logger.addHandler(fileHandler)

        #  添加下面一句,在记录日志之后移除句柄
        # self.logger.removeHandler(ch)
        # self.logger.removeHandler(fh)

        # 关闭处理器
        # conlHandler.close()
        fileHandler.close()

    def get_log(self):
        return self.logger

deal_operate.py文件:

# !/usr/bin python3   
# encoding  : utf-8 -*-                            
# @author   : ShanShan                            
# @software : PyCharm      
# @file     : deal_operate.py
# @Time     : 2021/5/31 16:06
from datetime import datetime

from commons.deal_log import Logs
from selenium import webdriver
from configs.config import ImagePath

class Operates():
    # 初始化页面的操作
    def __init__(self):
        """
        构造函数,创建必要的实例变量
        """
        self.driver = None
        self.log = Logs().get_log()  # 初始化一个log对象

    def openBrowser(self, driver='gc'):
        """
        打开不同的浏览器
        :param driver: gc=谷歌浏览器;ff=Firefox浏览器; ie=IE浏览器
        :return:
        """
        if driver == 'gc':
            self.driver = webdriver.Chrome()
        elif driver == 'ff':
            self.driver = webdriver.Firefox()
        elif driver == 'ie':
            self.driver = webdriver.Ie()
        else:
            self.log.info("不支持,请在此添加其他浏览器代码实现!")
            pass

        # 默认隐式等待10s
        self.driver.implicitly_wait(10)

    def click(self, *locator):
        """
        找到元素,并点击
        :param locator: 定位器
        :return:
        """
        try:
            self.driver.find_element(*locator).click()
        except Exception as e:
            self.log.error("定位点击元素失败!")

    def click_ele_exist(self, *locator):
        """
        当定为元素出现时,定位元素,并点击
        :param locator: 定位器
        :return:
        """
        try:
            self.driver.find_element(*locator)
            btns = self.driver.find_elements(*locator)
            # 如果出现用户协议弹出按钮
            if btns:
                btns[0].click()
        except Exception as e:
            self.log.error("定位有时出现的点击元素失败!")

    def input_text(self, value, *locator):
        """
        定位元素,并完成输入
        :param text:
        :param locator:
        :return:
        """
        try:
            self.driver.find_element(*locator).send_keys(str(value))
        except Exception as e:
            self.log.info("定位输入元素失败!")

    # 根据css定位的元素
    def by_js(self, css):
        try:
            self.driver.execute_script(f"document.querySelector('{css}')")
        except Exception as e:
            self.log.error("定位css元素失败")

    def switch_window(self, target_title):
        """
        切换到新窗口
        :param target_title:
        :return:
        """
        try:
            for handle in self.driver.window_handles:
                self.driver.switch_to.window(handle)
                # 判断切换到窗口句柄——-判断当前窗口的标题是否是:<header>中的<title>
                if target_title == self.driver.title:
                    self.log.info("切换窗口句柄到产品详情页")
                    break
        except Exception as e:
            self.log.error("切换新窗口失败!")

    def into_iframe(self, element):
        """
        进入iframe
        :param element:  ?
        :return:
        """
        try:
            ele = self.driver.find_element(element)
            self.driver.switch_to.frame(ele)
        except Exception as e:
            self.log.error("切换框架frame失败!")

    def get_text(self, *locator):
        """
        获取文本元素
        :param locator:
        :return:
        """
        try:
            ele = self.driver.find_element(*locator)
        except Exception as e:
            self.log.error("获取元素文本失败!")
        else:
            return ele.text

    def get_url(self, url=None):
        """
        打开网站
        :param url: 网站地址
        :return:
        """
        try:
            self.driver.get(url)
        except Exception as e:
            self.log.error("打开url失败!")

    def getImage(self, image_name):
        """
        生成用例失败的截图
        :param image_name: 截图的名称
        :return:
        """
        try:
            nowTime = datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
            NewPicture = ImagePath + '\\' + nowTime + '_' + image_name + '.png'   # 保存图片为png格式
            self.driver.get_screenshot_as_file(NewPicture)
        except Exception as e:
            self.log.error(u'截图失败!')


    def quit(self):
        """
        退出浏览器
        :return:
        """
        try:
            self.driver.quit()
        except Exception as e:
            self.log.error("退出浏览器失败!")

    # 后退
    def back(self):
        self.driver.back()

    # 前进
    def forword(self):
        self.driver.forword()



# if __name__ == '__main__':
#     # ele =BasePage()
#     path = os.path.dirname(os.path.abspath('.'))
#     print(path)

deal_yaml.py文件

import yaml

def read_yaml(key):
    """
    读取yaml文件
    :return:
    """
    try:
        # with open(path, encoding='utf-8') as f:
        #     eles = yaml.safe_load(f)
        path = DataPath  # 配置在congfig中的相对路径
        # path = '../data/test02.yaml'
        openYaml = open(path, 'r', encoding='UTF-8')
        datas = yaml.load(openYaml, Loader=yaml.FullLoader)

        data = datas[key]
        return data
    except Exception as e:
        print(u"未找到yaml文件")

2、config层
在这里插入图片描述
config.py文件

DataPath = "../data/test03.yaml"  # yaml文件的路径
LogPath = "../logs/"     # 日志文件存放路径
ImagePath = "../pictures/"   # 失败用例截图存放路径

3、data层
在这里插入图片描述
test03.yaml文件

xiaomiProcedure:  # 一个执行函数的数据
-
  title: 流程成功
  description: 流程成功的用例,用来测试正确的用户名和密码
  caseDatas:
    stepName1: 点击首页的登录按钮    # 也可也直接写在代码中
    login_btn: ['css selector','#J_siteUserInfo>a:nth-child(1)']  # 函数中定位的数据和输入的值
    stepName2: 登录页面输入用户名和密码
    user_proxy: ['css selector','.btn.btn-primary']
    user_input: ['xpath','//input[@name="account"]']
    psw_input: ['xpath','//input[@name="password"]']
    search_input: ['id','search']
    log_btn: ['css selector','.mi-button.mi-button--primary']
    first_item: ['css selector','.goods-list.clearfix>div:nth-child(1) a']
    buy_btn: ['css selector','.btn.btn-small.btn-primary.J_nav_buy']
    addCart_btn: ['css selector','.sale-btn']
    check_ele: ['id','J_miniHeaderTitle']
    

4、test_cases层

在这里插入图片描述

test_procedure002文件

import allure
import pytest
from  common.deal_operate import Operates
from common.deal_yaml import read_yaml
from common.deal_log import Logs
import time

# 首页(定位登录)-登录(输入用户和密码)- 首页(查询)- 商品页(定位第一个商品)-商品详情页(定位购物车)-购物车页面(认购)-购物车页面
@allure.feature('小米商城认购流程自动化')
class Test_indexPage:

    def setup_class(self):
        """
        构造函数,创建对象时会执行
        初始化浏览器
        :return:
        """
        print("执行setup_class-------")
        self.baseWeb = Operates()  # 创建一个对象
        self.baseWeb.openBrowser()    # 打开浏览器

        self.log = Logs().get_log()   # 获取一个对象的get_log()函数

        self.item = "小米11"

    def teardown_class(self):
        print("结束teardown_class—————————")
        self.baseWeb.quit()

    # 进入登录页
    @allure.story('认购流程')  # 同一个分组,比如成功和失败
    @pytest.mark.parametrize('data', read_yaml('xiaomiProcedure'))
    def test_procedure(self, data, get_user):
        # 首页
        print('获取data的数据类型:------', type(data))
        print('获取data数据:------', data)
        self.log.info("执行test_to_login-------")
        allure.dynamic.title(data['title'])  # allure:获取yaml文件中的title
        allure.description(data['description'])   # allure: 获取yaml文件中的description
        print('获取data的title数据:------', data['title'])

        self.baseWeb.get_url('https://www.mi.com/index.html')  # 小米首页
        caseData = data['caseDatas']

        with allure.step(caseData['stepName1']):
            self.baseWeb.click(*caseData['login_btn'])   # 点击首页的登录按钮
            self.baseWeb.click_ele_exist(*caseData['user_proxy'])  # 首页————点击协议声明的同意按钮(注意:这个跳转窗口不一定每次都出现,比如只有第一次才会出现)

        # 登录页面————输入登录用户名和密码
        with allure.step(caseData['stepName2']):
            name, psw = get_user
            self.baseWeb.input_text(name, *caseData['user_input'])
            self.baseWeb.input_text(psw, *caseData['psw_input'])
            time.sleep(2)

        # 登录页面——点击登录按钮
        with allure.step('点击登录页面的登录按钮'):
            self.baseWeb.click(*caseData['log_btn'])
            time.sleep(2)
            # 返回首页_进行查询
            allure.step('在首页进行查询')
            self.baseWeb.input_text(self.item+'\n', *caseData['search_input'])

        # 商品页面——点击第一个商品
        with allure.step('点击商品页面的第一个商品'):
            self.baseWeb.click(*caseData['first_item'])
            # 切换新窗口
            self.baseWeb.switch_window('小米11 12GB+256GB')

        # 商品详情页——点击 立即购买
        with allure.step("点击商品详情页上立即认购"):
            time.sleep(2)
            self.baseWeb.click(*caseData['buy_btn'])
            # 切换一个新窗口
            self.baseWeb.switch_window('小米11立即购买-小米商城')

        # 立即认购页面-点击 立即预定
        with allure.step("点击立即认购页面上的立即预定"):
            self.baseWeb.click(*caseData['addCart_btn'])

        # 购物车页面——校验
        with allure.step("校验是否进入认购页面"):
            try:
                res = self.baseWeb.get_text(*caseData['check_ele'])
                print("检查的数据:--", res)
                assert '儿童节' in res    # 校验是失败的
            except AssertionError:
                self.baseWeb.getImage('购物车页面')     # 添加了失败截图的功能
                # assert False
            else:
                assert True

conftest.py文件

import pytest
from selenium.webdriver.android import webdriver

@pytest.fixture(scope='session')
def get_user():
    Name = '15********'
    Pwd = '*******'
    print('调用Name------')
    yield Name, Pwd

5、run.py层

import os
import pytest

pytest.main(['-s', 'test_cases/test_procedure002.py', '--alluredir', './temp'])  # 生成allure报告,并放在./temp
os.system('allure generate ./temp -o ./report --clean')

6、pictures层
主要是用来存放用例失败时生成的截图(在test_procedure002.py 最后一步有添加此功能)
在这里插入图片描述

  • 4
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值