1.框架搭建

 优化前的框架:                                 优化后的框架:

UI自动化框架跟接口自动化框架相似,我们拿到一个项目之后,首先把需要的框架先搭建好,然后再一点一点去进行优化,不要急着一步到位,先把基本的流程跑通

gitignore文件:存放不上传到git上的内容

README文件:这个是框架的简单说明,供同事,领导等查看,以及自己时间长了记不清楚查看

requirements.txt文件:需要安装的第三方库

run.py文件:收集并运行用例

tests文件:存放所有的测试用例

data文件:存放所有的测试数据,如登录的账号密码

drivers文件:存放不同版本的驱动

output文件:存放allure生成的测试报告文件

pages文件:存放所有的页面元素操作封装的内容

report文件:pytest生成的测试报告(测试报告二选一即可)

setting文件:存放配置文件

2.UI自动化测试的地位

UI自动化测试在整个测试过程中占据的地位:

这个比例主要还是看公司,手工测试/自动化测试,60%/40%(比较厉害的公司)大多数公司自动化占据的比例还是相对要少的,自动化占20%左右

自动化测试又分:接口自动化/UI自动化/性能/安全  

                            70-80%/20-30%

优先实现正向用例,回归

3.UI自动化测试的特征

  1. 界面修改频繁(前端喜欢加<div></div>)
  2. 界面需求变动也大
  3. 运行速度很慢(浏览器打开速度慢,页面加载速度慢,占用CPU很多)
  4. 界面稳定性不够
  5. 界面排版是不方便做自动化

4.UI自动化测试流程

  1. 需求分析
  2. 用例设计:提供什么参数以及数据,测试步骤
  3. 用例评审(这步就不详细介绍了,主要看公司如何做)
  4. 编写代码
  5. 测试报告

4.1需求分析 

拿到一个项目后,首先是分析这个产品是做什么的,都有什么样的功能,例如课堂派web端,先整理出都分什么角色,分别对应什么功能,进行自动化测试前,肯定是手工测试已经进行了测试,并且功能稳定没有bug

4.2用例设计

编写UI自动化测试用例的流程:

  1. 手工测试用例编写
  2. 自动化测试用例代码编写

测试用例其实就是一个脚本script(这里指的不是代码中的脚本),作用是为了记录自己的一个思路,方便下次再去根据这个脚本去测试,方便管理,以免漏测等

我这里为了介绍就不一一去编写了,实际工作中需要把所有的用例测试点都转化成测试用例 ,有了测试用例之后,我们就可以把用例转化成自动化测试代码了

4.3编写代码

根据编写的测试用例,进行代码编写,如登录功能

步骤:

  1. 输入登录url:https://v4.ketangpai.com/Home/User/login.html
  2. 输入用户名:aaa  name="account"
  3. 输入密码:123  name="pass"
  4. 点击登录按钮:class="btn-btn" (虽然找到两个相同元素,但fide_element只取第一个,正好我们需要的是第一个)
  5. 进行断言

 具体代码如下:

from selenium import webdriver
from selenium.webdriver.common.by import By


class TestSignIn:
    def test_sing_in_01(self):
        username = 'aaa'
        password = '123'
        expected = '密码有效长度是6到30个字符'
        with webdriver.Chrome(executable_path=r'E:\lemon\lianxi\day39_web框架搭建\chromedriver_96.exe') as browser:
            browser.implicitly_wait(5)
            browser.maximize_window()
            # 访问url
            url = 'https://v4.ketangpai.com/Home/User/login.html'
            browser.get(url)
            # 输入用户名
            el_user = browser.find_element(By.NAME, 'account')
            el_user.clear()
            el_user.send_keys(username)
            # 输入密码
            el_pwd = browser.find_element(By.NAME, 'pass')
            el_pwd.clear()
            el_pwd.send_keys(password)
            # 点击登录按钮
            browser.find_element(By.CSS_SELECTOR, '.btn-btn').click()
            # 获取登录后报错的文本
            actual = browser.find_element(By.CSS_SELECTOR, '.error-tips').text
            # 通过属性名称获取属性值
            # actual = browser.find_element('xpath', '//p[@class="error-tips"]').get_attribute('name')
            assert expected == actual

注意:

executable_path=r'E:\lemon\lianxi\day39_web框架搭建\chromedriver_96.exe'这个是可以不用填写的前提是:把该驱动放到了环境变量中了,前面有介绍,可以参考此篇文章:selenium-webdriver环境搭建_晒不黑的黑煤球的博客-CSDN博客
之所以编写此名代码,是在实际项目中,可能会测试多个不同版本的驱动与浏览器时方便管理

这是拿到一个项目后,先保证编写的代码可以顺利的执行没有报错,再去进行优化,我这里是演示就不一一编写所有用例代码了,编写过程是一样的,就是输入的数据与断言有所有更改

接下来,我们就对代码进行各种封装及优化

4.3.1优化代码

优化方向:

  • 更好用
  • 更易懂
  • 维护代码方便
  • 更通用
  • 扩展性

优化点:

  1. 隔离测试数据,当需要添加或修改数据时,可以在单独的模块中进行修改
  2. 浏览器管理可以重复使用,所以可以进行单独封装
  3. 需要把驱动进行隔离管理:1.可以存储多个浏览器驱动,2.想用哪个用哪个
  4. base url域名ip
  5. 登录操作可以重复使用(项目通用)
  6. 登录操作:已经登录过了,不需要重新再登录,记住用户登录状态
  7. PO模式

优化第一点:

测试数据我们可以从配置文件,或者execl表格获取,也可以直接新建一个模块来保存数据,我这里新建了一个login_data.py来存储

login_fail = {'username': 'aaa', 'password': '123', 'expected': '密码有效长度是6到30个字符'}

 自动化测试用例修改如下:

 

 优化第二点:

因为我们执行测试用例之前是先打开浏览器操作,用例执行后再进行浏览器关闭,因此把浏览器的封装放到夹具中最合适,之前我们介绍过共享fixture,就是把所有的夹具放到固定的conftest.py文件中,这样不需要导入,pytest会自动调用

注意:conftest.py文件不能放到其他包下,放在根目录或tests包下即可,否则会报错

import pytest
from selenium import webdriver


# 打开浏览器
def get_browser():
    # 因为要在测试用例后执行闭关浏览器,因为不要使用with的打开方式
    browser = webdriver.Chrome(executable_path=r'E:\lemon\lianxi\day39_web框架搭建\chromedriver_96.exe')
    browser.implicitly_wait(5)
    browser.maximize_window()
    return browser

# 设置夹具
@pytest.fixture()
def browser():
    driver = get_browser()
    yield driver
    driver.quit()

自动化测试用例修改如下:

把相关打开浏览器,设置隐性等待,窗口最大化的代码删除掉,只传入夹具browser即可

优化第三点:

把所有的驱动放到一个目录中,这样方便管理与操作,我新建了drivers目录存放驱动,新建了setting包,config.py配置文件(为了动态获取驱动的目录)

config.py代码如下:

import os

# 获取config.py当前文件的路径
curren_path = os.path.abspath(__file__)
# 配置文件目录的路径
config_dir = os.path.dirname(curren_path)
# setting包的路径
cf_dir = os.path.dirname(config_dir)
# host地址
host = 'https://v4.ketangpai.com/'
# 驱动的路径
driver_dir = os.path.join(cf_dir, 'drivers')

修改conftest.py代码如下:

4.3.2PO模式

PO/POM模式(Page Object 或Page Object Model)是UI测试模式,字面意思就是页面对象模型,从本质上来说就是把页面的元素操作封装成类当中的方法,在测试过程中不再需要关注页面的结构,只需要关心执行的操作,这样测试用例中的代码看起来简单明了易懂

具体操作就是上面优化第七点的过程,好处就是:

  1. 测试用例中的代码变更加精简
  2. 代码可读性变强
  3. 页面操作封装完成,进行隔离管理(也就是分层设计理念):前端发生变化,只需要维护PO封装后的代码,如果测试数据修改了,只需要修改测试数据即可
  4. PO封装的页面操作是可以重复使用的

PO的封装有以下原则,如果遵守的话会达到更好的效果:

最后两条可以不参考

  • 页面封装里不应该包含断言或者测试,否则就不能做到页面操作和测试操作分离
  • 唯一可以放在页面当中的测试是判断一个元素是否能找到
  • 不需要封装所有的页面操作,用到什么封装什么
  • 页面封装也可以使用组件方式:如导航栏,object,footer object等在多个页面重复出现的,使用组件可以增强代码复用性
  • 封装方法返回self,其他po,或者基础数据结构,string,dates,不应该element对象等
  • 如果同一个操作引发不同的结果,可以封装多个方法,如:login_failed,login_success

优化第七点: 

就是使用PO模式进行封装代码,把页面操作的相关代码都封装到单独的类方法中,具体操作如下

新建page目录下login_page.py文件,代码如下:

from selenium.webdriver.common.by import By


class LoginPage:
    def __init__(self, browser):
        self.browser = browser

    def login(self, username, password):
        """登录"""
        # 输入用户名
        el_user = self.browser.find_element(By.NAME, 'account')
        el_user.clear()
        el_user.send_keys(username)
        # 输入密码
        el_pwd = self.browser.find_element(By.NAME, 'pass')
        el_pwd.clear()
        el_pwd.send_keys(password)
        # 点击登录按钮
        self.browser.find_element(By.CSS_SELECTOR, '.btn-btn').click()

    def get_error_tips(self):
        """获取错误信息"""
        return self.browser.find_element(By.CSS_SELECTOR, '.error-tips').text

 自动化测试用例修改如下:

此步还可以进一步优化,设置一个夹具,依赖browser夹具,然后测试用例就不需要初始化loginpage了,直接调用login_page夹具即可

修改conftest.py代码如下:

import pytest
import os
from selenium import webdriver
from setting import config
from pages.login_page import LoginPage

driver = os.path.join(config.driver_dir, 'chromedriver_96.exe')


# 打开浏览器
def get_browser():
    # 因为要在测试用例后执行闭关浏览器,因为不要使用with的打开方式
    browser = webdriver.Chrome(executable_path=driver)
    browser.implicitly_wait(5)
    browser.maximize_window()
    return browser


# 设置夹具
@pytest.fixture()
def browser():
    driver = get_browser()
    yield driver
    driver.quit()

# 依赖上一个夹具(上一个夹具是这个夹具的前置条件)
@pytest.fixture()
def login_page(browser):
    return LoginPage(browser)

夹具的套娃:(一个夹具可以依赖另一个夹具)

夹具 --> 用例的依赖关系

夹具 --> 夹具的依赖关系

自动化测试用例修改如下: 

这个封装的过程就是PO模式,所有的页面操作代码都不会编写在测试用例代码中,而是放在一个类中独立管理

优化第四点: 

url也放到login_page.py文件中,增加一个加载登录页面的方法,代码如下

from selenium.webdriver.common.by import By
from setting import config


class LoginPage:
    url = config.host + 'User/login.html'

    def __init__(self, browser):
        self.browser = browser

    def load(self):
        """加载页面"""
        self.browser.get(self.url)

    def login(self, username, password):
        """登录"""
        # 输入用户名
        el_user = self.browser.find_element(By.NAME, 'account')
        el_user.clear()
        el_user.send_keys(username)
        # 输入密码
        el_pwd = self.browser.find_element(By.NAME, 'pass')
        el_pwd.clear()
        el_pwd.send_keys(password)
        # 点击登录按钮
        self.browser.find_element(By.CSS_SELECTOR, '.btn-btn').click()

    def get_error_tips(self):
        """获取错误信息"""
        return self.browser.find_element(By.CSS_SELECTOR, '.error-tips').text

自动化测试用例修改如下:

from data.login_data import login_fail


class TestSignIn:
    def test_sing_in_01(self, login_page):
        # 数据
        username = login_fail['username']
        password = login_fail['password']
        expected = login_fail['expected']
        # 获取实际结果
        login_page.load()
        login_page.login(username, password)
        actual = login_page.get_error_tips()
        # 断言
        assert expected == actual

调用过程图如下:

剩下还没优化的点,在之后的文章中会详细介绍,此篇就介绍到这里

4.4测试报告

获取当前系统时间戳来生成测试报告名+时间戳来存储测试报告,具体代码如下:

import pytest
import datetime

# 获取当前时间戳
ts = datetime.datetime.now().strftime('%Y-%m-%d-%H-%M-%S')
# 路径拼接
filename = f'reports/report-{ts}.html'
# 收集并执行用例(注意加上[])
pytest.main([f'--html={filename}'])
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐