pythonUi自动化框架 po-unittest+yaml+HTMLTestRunner

发布时间 2023-03-29 16:27:51作者: wn_garden

po自动化框架简介


page object层

1.对webDriver对象进行二次封装,简化操作
2.针对不同页面 封装对于的元素(包含断言要用的元素) 和 行为

WebDriver封装-base基础

"""
bese页面,用于driver的方法封装
操作方法封装:
    打开网页
    关闭驱动
    获得driver对象
    find_element
    find_elements
    send_keys
    click
    选择菜单
"""
import time
from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

from setting import driver_path, imp_wait
from selenium.webdriver.chrome.service import Service


class Base:

    def __init__(self, path=driver_path, wait=imp_wait, driver=None):
        # 初始化时判断是否传入driver对象,不传默认创建一个
        if driver is None:
            self.driver = webdriver.Chrome(service=Service(path))
            self.driver.maximize_window()
        else:
            self.driver = driver

        self.driver.implicitly_wait(wait)

    def get_driver(self):
        return self.driver

    def open(self, url):
        self.driver.get(url)

    def close(self):
        self.driver.close()

    @classmethod
    def speed(cls, wait_name):
        time.sleep(wait_name)

    def find_ele(self, locator: tuple):
        return self.driver.find_element(*locator)

    def find_eles(self, locator):
        return self.driver.find_elements(*locator)

    def send(self, locator, value):
        self.find_ele(locator).send_keys(value)

    def click(self, locator):
        self.find_ele(locator).click()

    # 显示等待
    def find_ele_display(self, locator, wait=5):
        ele = WebDriverWait(self.driver, wait).until(EC.presence_of_element_located(locator))
        return ele

    # 根据文本选择菜单
    def menu(self, parent_loc, locator, text):
        self.click(parent_loc)
        for ele in self.find_eles(locator):
            if text == ele.text:
                ele.click()


if __name__ == '__main__':
    pass

登录页面封装

"""
登录页面 http://127.0.0.1:8000/login/
属性:
    用户名
    密码
    验证码
    登录按钮
行为:
    登录操作

断言元素:
    登出按钮
    登录错误提示
"""
from po.base import Base


class Login(Base):

    # 用户名-密码-验证码-登录按钮
    ele_name = ('id', 'LAY-user-login-username')
    ele_pwd = ('id', 'LAY-user-login-password')
    ele_verif = ('id', 'LAY-user-login-vercode')
    ele_button = ('id', 'loginButton')
    # 登出按钮-错误信息(登录失败时)
    ele_logout = ('css selector', 'a[href="/logout/"]')
    ele_error = ('css selector', 'div[class = "layui-layer-content layui-layer-padding"]')

    def do_login(self, url='http://127.0.0.1:8000/login/',
                 name='admin', pwd='123456', verif='0'):
        self.open(url)
        if name:
            self.send(self.ele_name, name)
        if pwd:
            self.send(self.ele_pwd, pwd)
        if verif:
            self.send(self.ele_verif, verif)
        self.click(self.ele_button)

        self.speed(0.5)

    def assert_success(self):
        try:
            info = self.find_ele(self.ele_logout).text
            self.close()
            return True
        except:
            self.close()
            print('登录成功断言--失败')

    def assert_fails(self, expect):
        try:
            info = self.find_ele(self.ele_error).text
            if expect in info:
                print('登录失败测试--成功')
                self.close()
                return True
            else:
                print(f'期望:{expect}  实际:{info}')
                print('登录失败测试--失败')
                self.close()
                return False
        except:
            print('登录失败测试获取异常信息--失败')
            self.close()
            return False


if __name__ == '__main__':
    url = 'http://127.0.0.1:8000/login/'

    login1 = Login()
    login1.do_login(url, "qqq", "1", "1")
    login1.assert_fails('用户名或密码错误')

    # login2 = Login()
    # login2.do_login(url, "qqq", "1", "")
    # login2.assert_success()
    # # login2.assert_fails('请输入验证码')

需求申请页面封装

"""page object
需求申请页面:http://127.0.0.1:8000/mainpage/
属性:
    主菜单
    子菜单
    部门
    日期
    名称
    管理系统
    需求类型
    需求描述
方法:
    提交需求描述
"""
from po.base import Base
from po.login import Login


class Application(Base):
    # 菜单栏选择-需求管理-需求申请
    ele_menu_parent = ('css selector', 'li.layui-nav-item cite')
    ele_menus = ('css selector', 'a[action$="_order/"]')
    # 第一个输入框文字(用于选时间后点击)
    ele_titles = ('css selector', 'label.layui-form-label')
    # 所有 输入框的元素列表
    ele_inputs = ('css selector', 'input.layui-input')
    # 部门下拉框下的选项-单选框-描述-提交/重置
    ele_dd = ('css selector', 'dd[lay-value^="00"]')
    ele_radios = ('css selector', 'div[class^="layui-unselect layui-form-radio"]')
    ele_descript = ('css selector', 'textarea.layui-textarea')
    ele_buttons = ('css selector', 'button[class^="layui-btn"]')
    # 提交信息提示
    # ele_erro = ('css selector', 'div[class="layui-layer-content layui-layer-padding"]')
    ele_info = ('css selector', 'div[class="layui-layer-content layui-layer-padding"]')
    # 提示信息-确然按钮
    button = ('css selector', 'a.layui-layer-btn0')

    def select_menu(self):
        # 选择菜单
        self.menu(self.ele_menu_parent, self.ele_menus, '需求申请')
        # 切换frame
        self.driver.switch_to.frame('mainframe')

    def __click_button(self, button):
        for element in self.find_eles(self.ele_buttons):
            if element.text in button:
                element.click()

    def application(self, department=None, date=None, title=None, server=None, radio=None, descript=None):
        # 需求部门
        elements = self.find_eles(self.ele_inputs)
        if department:
            elements[0].click()
            self.speed(0.5)
            dep_li = self.find_eles(self.ele_dd)
            # 部门下的下拉框,按文字选择
            for ele in dep_li:
                # print('下拉框:', ele.text)
                if department in ele.text:
                    ele.click()
                    break

        # 申请时间
        if date:
            elements[1].send_keys(date)
            self.find_eles(self.ele_titles)[0].click()
        # 需求名字
        if title:
            elements[2].send_keys(title)
        # 关联系统
        if server:
            elements[3].send_keys(server)
        # 单选框
        if radio:
            for ele in self.find_eles(self.ele_radios):
                if ele.text == radio:
                    ele.click()
                    break
        # 描述
        if descript:
            self.send(self.ele_descript, descript)
        # 点击按钮
        self.__click_button('提交')

    def assert_success(self, msg='需求登记成功'):
        self.speed(0.5)
        info = self.find_ele_display(self.ele_info).text
        print(info)
        self.speed(0.5)
        self.__click_button('重置')

        if msg in info:
            return True

        return False

    def assert_fails(self, msg='日期不在允许范围内'):
        self.speed(0.5)
        # 填错 报提示信息
        info = self.find_ele_display(self.ele_info).text
        print('错误提示:', info, ' 实际:', msg)
        try:
            self.driver.implicitly_wait(0.2)
            self.find_ele_display(self.button, 0.2).click()
        except:
            # print('错误提示无确认按钮')
            pass
        self.speed(0.5)
        # 防止日期弹框
        self.find_eles(self.ele_titles)[0].click()
        self.__click_button('重置')

        if msg in info:
            return True

        return False


if __name__ == '__main__':
    url = 'http://127.0.0.1:8000/login/'
    page = Login()
    page.do_login(url, 'admin', '123456', '1')
    dr = page.get_driver()
    app=Application(driver=dr)
    app.select_menu()
    app.application()
    print(app.assert_fails(msg="选择部门"))
    app.close()

test层

登录测试用例

 import unittest
import ddt
import yaml
from po.login import Login
from setting import login_success_file, login_url, login_fails_file
import warnings

"""
测试用例
登录页面 http://127.0.0.1:8000/login/
"""
@ddt.ddt
class TestLogin(unittest.TestCase):
    @classmethod
    def setUpClass(cls) -> None:
        warnings.simplefilter("ignore", ResourceWarning)
        cls.url = login_url

    def setUp(self) -> None:
        self.login = Login()

    @ddt.file_data(login_success_file)
    def test_login_success(self, name, pwd, verif):
        self.login.do_login(self.url, name, pwd, verif)
        flag = self.login.assert_success()
        self.assertTrue(flag)

    @ddt.file_data(login_fails_file)
    def test_login_fails(self, name, pwd, verif, error):
        self.login.do_login(self.url, name, pwd, verif)
        flag = self.login.assert_fails(error)
        self.assertTrue(flag)


if __name__ == '__main__':
    unittest.main()

需申请测试用例

import time
import unittest
import ddt

from common.util import exec_code
from po.application import Application
from po.login import Login
from setting import application_fails_file as file_fail, application_success_file as file_suc

"""
测试用例
需求申请页面:http://127.0.0.1:8000/mainpage/
"""
@ddt.ddt
class TestApplication(unittest.TestCase):
    @classmethod
    def setUpClass(cls) -> None:

        login = Login()
        login.do_login()
        dr = login.driver
        cls.app = Application(driver=dr)
        cls.app.select_menu()

    @classmethod
    def tearDownClass(cls) -> None:
        cls.app.close()

    @ddt.file_data(file_suc)
    def test_require_success(self, **kwargs):
        time.sleep(0.5)
        # 执行yaml中的 时间代码
        kwargs['date'] = exec_code(kwargs['date'])
        print('入参',kwargs)
        self.app.application(kwargs['department'],
                        kwargs['date'],
                        kwargs['title'],
                        kwargs['server'],
                        kwargs['radis'],
                        kwargs['descript'])
        flag = self.app.assert_success()
        self.assertTrue(flag)

    @ddt.file_data(file_fail)
    def test_require_fails(self, **kwargs):
        time.sleep(0.5)
        # 执行yaml中的 时间代码
        kwargs['date'] = exec_code(kwargs['date'])
        print('入参', kwargs)
        self.app.application(kwargs['department'],
                        kwargs['date'],
                        kwargs['title'],
                        kwargs['server'],
                        kwargs['radis'],
                        kwargs['descript'])
        flag = self.app.assert_fails(kwargs['msg'])
        self.assertTrue(flag)


if __name__ == '__main__':
    unittest.main()

common公共层

公共方法

import datetime
import os

from setting import sql_file


def exec_code(info: str):
    """
    用于执行yaml文件中的代码---比如下方时间
    <datetime.date.today().strftime('%Y-%m-%d')>
    """
    if info:
        if info.startswith('<') and info.endswith('>'):
            print(info[1:-1])
            exec_info = eval(info[1:-1])
            return exec_info
    return info


def init_mysql(path, host='localhost'):
    os.system(f'mysql -h{host} -P3306 -uroot -p123456  -Dtestproject<{path}')
    print('--mysql初始化成功--')


if __name__ == '__main__':
    # info = "<datetime.date.today().strftime('%Y-%m-%d')>"
    # new_inf = exec_code(info)
    # print(new_inf)
    # init_mysql(sql_file)

主执行方法main.py

"""
启动入口--测试套件suite启动
"""
import unittest
from common.util import init_mysql
from setting import sql_file
from common.HTMLTestRunner import HTMLTestRunner

if __name__ == '__main__':
    # 创建套件
    suite = unittest.TestSuite()
    # 加入目录下测试单元
    discover = unittest.defaultTestLoader.discover(start_dir='tests', pattern='test*.py')
    suite.addTests(discover)
    # 执行并生成报告
    with open('./report.html', 'wb') as f:
        runner = HTMLTestRunner(f, verbosity=2)
        runner.run(suite)

    # 初始化mysql数据
    init_mysql(sql_file)

数据层

每个po页对于的成功和失败用例数据,分别放置

--其他页面也是同样--
需求申请页,成功数据

-
  department: 人力部门
  date: <datetime.date.today().strftime('%Y-%m-%d')>
  title: 一号需求测试
  server: 小程序
  radis: 新增需求
  descript: 人力资源部人手不够需要开发一个自动筛选简历的web网页
-
  department: 保卫部
  date: <datetime.date.today().strftime('%Y-%m-%d')>
  title: 二号需求测试
  server: 小程序
  radis: 新增需求
  descript: 人保资源部人手不够需要开发一个自动筛选地图的功能

需求申请页,失败数据

-
 department:
 date:
 title:
 server:
 radis:
 descript:
 msg: 选择部门
-
 department: 人力部门
 date: "2022-04-01"
 title: 一号需求测试
 server: 小程序
 radis: 新增需求
 descript: 人力资源部人手不够需要开发一个自动筛选简历的web网页
 msg: 日期不在允许范围内

公共变量Setting

import os

# 登录url
login_url = 'http://127.0.0.1:8000/login/'

PATH = os.path.dirname(__file__)
# ----------浏览器驱动----------
driver_path = os.path.join(PATH, 'datas', 'chromedriver.exe')
imp_wait = 5
# ---------数据库初始化文件----------
sql_file = os.path.join(PATH, 'datas', 'init_testproject.sql')
# ---------测试数据文件-------------
login_success_file = os.path.join(PATH, 'datas', 'loginSuccess.yaml')
login_fails_file = os.path.join(PATH, 'datas', 'loginFails.yaml')
application_success_file = os.path.join(PATH, 'datas', 'applicationSuccess.yaml')
application_fails_file = os.path.join(PATH, 'datas', 'applicationFails.yaml')
management_success_file = os.path.join(PATH, 'datas', 'managementSuccess.yaml')