2022软件测试技能 Web自动化测试框架之Python Unittest从入门到精通

系列文章目录

提示:阅读本章,请先阅读目录


文章目录

  • 系列文章目录
  • 前言
  • 一、Unittest 框架
    • 1. TestCase 测试用例
    • 2. TestFixture 测试夹具
    • 3. TestSuite 测试套件
      • 添加单个测试用例
      • 添加多个测试用例
      • 加载类下的所有用例
      • 加载某个路径下的所有测试用例
    • 4. TestRunner 测试运行器
      • 1. TextTestRunner 文本测试报告生成器
      • 2. 生成Html报告
    • 5. 装饰器
      • 1. @unittest.skip 强制跳过
      • 2. @unittest.skipIf 符合条件,则跳过
      • 3. @unittest.skipUnless 符合条件,则不跳过
  • 二、POM 设计模型
    • 1. 什么是POM 设计模型?
    • 2. POM 分为四层设计架构
      • 1. base 基类
      • 2. page 页面对象类
      • 3. testcase 测试用例层
      • 4. testdata 测试数据
    • 3. 如何基于POM进行自动化框架架构?
  • 三、数据驱动和关键字驱动
    • 1. 数据驱动
    • 2. 关键字驱动
  • 更新日志


前言

unittest原名为PyUnit,是由java的JUnit衍生而来。unittest是python内置的单元测试框架,具备编写用例、组织用例、执行用例、输出报告等自动化框架的条件。unittest作为官方的测试框架,在测试方面非常基础,可以在此基础上进行二次开发。


一、Unittest 框架

python的内置模块
unittest框架 = 测试模块+测试管理模块+测试统计模块

导入方式:
import unittest

四大组件:

  1. TestCase 测试用例
  2. TestFixture 测试夹具
  3. TestSuite 测试套件
  4. TestRunner 测试运行器

下面就讲解四大组件

1. TestCase 测试用例

规则:

  1. 用例类必须继承unittest.TestCase,并且以Test开头
  2. 用例方法,function,必须以test开头
  3. 用例执行的顺序是根据ASCII码排序,如果是按照顺序,可以用test_01,test_02这样的方法命名方法
import unittest
from selenium import webdriver


class TestCaseLogin(unittest.TestCase):

    @classmethod
    def setUpClass(cls) -> None:
        """
        初始化,打开浏览器,设置浏览器最大化
        :return:
        """
        cls.driver = webdriver.Chrome()
        cls.driver.maximize_window()

    def test_01_open(self):
        """
        打开项目
        :return:
        """
        self.driver.get('http://www.baidu.com/login')

    def test_02_input(self):
        """
        对账号,密码框,进行赋值
        :return:
        """
        username = self.driver.find_element_by_id('username')
        password = self.driver.find_element_by_id('password')
        username.send_keys('admin')
        password.send_keys('abc123456')

    def test_03_submit(self):
        """
        点击登录按钮
        :return:
        """
        submit = self.driver.find_element_by_id('submit')
        submit.click()

这是一个标准的测试用例类,文件名为:
test_case_login.py
一般存放在
testcase 文件夹
路径为:
project/testcase/test_case_login.py

2. TestFixture 测试夹具

执行测试用例的前置操作,后置操作,相当于把测试用例夹在中间,因此得名,测试夹具

import unittest
from selenium import webdriver


class TestCaseLogin(unittest.TestCase):

    @classmethod
    def setUpClass(cls) -> None:
        """
        初始化,打开浏览器,设置浏览器最大化
        类的第一次执行,会先执行setUpClass,前置操作
        :return:
        """
        cls.driver = webdriver.Chrome()
        cls.driver.maximize_window()
        
    @classmethod
    def tearDownClass(cls) -> None:
        """
        类的结束之前,会先执行tearDownClass 后置操作
        :return: 
        """
        pass
    
    def setUp(self) -> None:
        """
        每个function执行之前,都会执行setUp
        :return: 
        """
        pass
    
    def tearDown(self) -> None:
        """
        每个function结束之前,tearDown
        :return: 
        """
        pass

3. TestSuite 测试套件

TestSuite:属于比较灵活的,可以添加单个,多个测试用例,主要测试某个类的用例,或者某个类
TestLoader:属于批量加载测试用例库,主要用于某个版本发布的时候,批量运行

添加单个测试用例

# test_case_login.py
import unittest
from selenium import webdriver


class TestCaseLogin(unittest.TestCase):

    def setUp(self) -> None:
        """
        每个function执行之前,都会执行setUp
        :return:
        """
        print('方法执行之前,执行了setUp')

    def tearDown(self) -> None:
        """
        每个function结束之前,tearDown
        :return:
        """
        print('方法结束之前,执行了tearDown')

    def test_01_open(self):
        """
        打开项目
        :return:
        """
        print('执行了open')

    def test_02_input(self):
        """
        对账号,密码框,进行赋值
        :return:
        """
        print('执行了input')

    def test_03_submit(self):
        """
        点击登录按钮
        :return:
        """
        print('执行了submit')
# run.py
from test_case_login import TestCaseLogin
import unittest

# 实例化一个套件
suite = unittest.TestSuite()
# 往套件,添加一个测试用例
suite.addTest(TestCaseLogin('test_02_input'))
# 套件的run,需要传入一个Result,所以我们实例化一个就可以了
result = unittest.TestResult()
# 运行suite的run,实际上是suite的run会去调用所有测试用例的run
suite.run(result)
运行结果:
方法执行之前,执行了setUp
执行了input
方法结束之前,执行了tearDown

Process finished with exit code 0

添加多个测试用例

from test_case_login import TestCaseLogin
import unittest

suite = unittest.TestSuite()
# suite.addTest(TestCaseLogin('test_02_input'))
suite.addTests([
    TestCaseLogin('test_01_open'),
    TestCaseLogin('test_03_submit')
])
result = unittest.TestResult()
suite.run(result)

运行结果:
方法执行之前,执行了setUp
执行了open
方法结束之前,执行了tearDown
方法执行之前,执行了setUp
执行了submit
方法结束之前,执行了tearDown

加载类下的所有用例

from test_case_login import TestCaseLogin
import unittest

# loadTestsFromTestCase 加载类下面的所有用例
testcase = unittest.TestLoader().loadTestsFromTestCase(
    TestCaseLogin
)
# 实例化一个result
result = unittest.TestResult()
# 运行
testcase.run(result)
运行结果:
方法执行之前,执行了setUp
执行了open
方法结束之前,执行了tearDown
方法执行之前,执行了setUp
执行了input
方法结束之前,执行了tearDown
方法执行之前,执行了setUp
执行了submit
方法结束之前,执行了tearDown

加载某个路径下的所有测试用例

我们把目录结构优化一下
并增加了test_case_reg.py

2022软件测试技能 Web自动化测试框架之Python Unittest从入门到精通_第1张图片

# testcase/test_case_login.py
import unittest


class TestCaseLogin(unittest.TestCase):

    def test_01_open(self):
        """
        打开项目
        :return:
        """
        print('执行了open')

    def test_02_input(self):
        """
        对账号,密码框,进行赋值
        :return:
        """
        print('执行了input')

    def test_03_submit(self):
        """
        点击登录按钮
        :return:
        """
        print('执行了submit')
# testcase/test_case_reg.py
import unittest


class TestCaseReg(unittest.TestCase):

    def test_01_reg(self):
        """
        打开项目
        :return:
        """
        print('执行了reg注册')

这样的话,如果我们想运行testcase目录下的所有测试用例
就可以用 unittest.defaultTestLoader.discover

import unittest

# discover 加载路径,start_dir = 路径,pattern = 匹配规则
suite = unittest.defaultTestLoader.discover(
    start_dir='testcase',
    pattern='test_*.py'
)
result = unittest.TestResult()
suite.run(result)
运行结果:
执行了open
执行了input
执行了submit
执行了reg注册

4. TestRunner 测试运行器

TestRunner:执行测试用例,并输出结果

1. TextTestRunner 文本测试报告生成器

import unittest
from testcase.test_case_login import TestCaseLogin

# 实例化套件suite
suite = unittest.TestSuite()
# 添加单个测试用例
suite.addTest(TestCaseLogin('test_01_open'))
# 实例化TextTestRunner运行器,以文本形式展示出来
runner = unittest.TextTestRunner()
# 执行
runner.run(suite)
运行结果:
执行了open
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

Process finished with exit code 0

在 Ran 1 test in 0.000s 上面

有 一个点 则代表:用例执行成功
有 E 则代表:用例执行失败
有 F 则代表:用例执行异常(断言失败)
有 S 则代表:用例执行了装饰器skip,跳过不执行

2. 生成Html报告

方式一:
可以使用unittest 最基础的测试报告模板
HtmlTestRunner,但是只支持python2,如果要支持python3,就是要修改代码,让里面的语法支持python3

2022软件测试技能 Web自动化测试框架之Python Unittest从入门到精通_第2张图片

方式二:
可以安装插件 unittestreport
可以生成比较美观的html报告,有3种模板,并且还可以支持多线程

2022软件测试技能 Web自动化测试框架之Python Unittest从入门到精通_第3张图片

方式三:
可以安装插件 BeatifulReport
有四种主题,跟 unittestreport差不多,但是unittestreport比较强大一点

2022软件测试技能 Web自动化测试框架之Python Unittest从入门到精通_第4张图片
方式四:

企业测试报告定制,可以让开发团队,专门开发,独立专业的Html测试报告

方式五:

可以结合pytest+allure,生成更加美观,详细的测试报告,并且allure支持自定义配置,可以给某个企业定制模板

本质上,unittest的规则,适用于pytest的规则,只是有一些地方有差异

比如在测试夹具
unittest,是setUp ,而pytest是 setup
所以要结合pytest的话,这个地方需要注意一下
至于其他的命名规则,文件路径等,几乎一样

5. 装饰器

1. @unittest.skip 强制跳过

2. @unittest.skipIf 符合条件,则跳过

3. @unittest.skipUnless 符合条件,则不跳过

目录结构:

2022软件测试技能 Web自动化测试框架之Python Unittest从入门到精通_第5张图片

# testcase/test_case_login.py

import unittest


class TestCaseLogin(unittest.TestCase):

    def test_01_open(self):
        """
        打开项目
        :return:
        """
        print('执行了open')

    @unittest.skip
    def test_02_input(self):
        """
        对账号,密码框,进行赋值
        :return:
        """
        print('执行了input')

    @unittest.skipIf(3 > 2, '符合条件,跳过')
    def test_03_submit(self):
        """
        点击登录按钮
        :return:
        """
        print('执行了submit')
# testcase/test_case_reg.py

import unittest


class TestCaseReg(unittest.TestCase):

    @unittest.skipUnless(3 > 2, '条件成立,不跳过')
    def test_01_reg(self):
        """
        打开项目
        :return:
        """
        print('执行了reg注册')
# run.py

import unittest

suite = unittest.defaultTestLoader.discover(
    start_dir='testcase',
    pattern='test_*.py'
)
runner = unittest.TextTestRunner()
runner.run(suite)
运行结果:

执行了open
执行了reg注册
..s.
----------------------------------------------------------------------
Ran 4 tests in 0.001s

OK (skipped=1)

二、POM 设计模型

1. 什么是POM 设计模型?

POM设计模式,即 page object model 页面对象模型,是web自动化测试应用最广泛的设计模型之一。

其设计思路,就是一个web项目有很多页面,即把一个页面当成一个页面对象来进行设计

python:什么是对象?
通过类class 去描述一组对象,对象=属性+方法

每个页面都会有相同的属性及方法:
比如,点击,输入,元素定位的方法

2. POM 分为四层设计架构

1. base 基类

base/base.py

每个页面会有相同的属性及方法
例如:
点击,输入,定位元素等

2. page 页面对象类

page/page_login.py
page/page_reg.py

针对每个页面去定义页面类,页面类会继承基类
比如:
登录页面,输入账号,密码,点击登录

3. testcase 测试用例层

测试用例层,需要继承unittest.TestCase 类
主要包含项目的业务流程
例如:
从登录之后查看购物车,并把购物车清空

4. testdata 测试数据

测试数据,集中存放,管理测试用例所需要的数据

3. 如何基于POM进行自动化框架架构?

项目目录结构:

2022软件测试技能 Web自动化测试框架之Python Unittest从入门到精通_第6张图片

base/base.py

基类,封装所有页面共用的属性及方法

# -*- coding: utf-8 -*-
# @Time : 2022/7/9 10:43
# @Author : ZhongShaoFeng
# @File: base.py
from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait


class Base:

    def __init__(self, url):
        """
        初始化,需传入url,打开项目
        :param url:
        """
        self.driver = webdriver.Chrome()
        self.driver.maximize_window()
        self.driver.get(url)

    def base_find_element(self, loc, timeout=30):
        """
        查找元素,找到之后返回元素
        :param loc: tuple (By.xx, 'xxx')
        :param timeout: 超时时间,default=30
        :return: element 元素
        """
        return WebDriverWait(
            driver=self.driver,
            timeout=timeout,
            poll_frequency=0.5
        ).until(
            lambda x: x.find_element(*loc)
        )

    def base_click(self, loc):
        """
        点击click
        :param loc:
        :return:
        """
        self.base_find_element(loc).click()

    def base_input(self, loc, val):
        """
        输入send_keys
        :param loc:
        :param val: 传入输入值
        :return:
        """
        self.base_find_element(loc).send_keys(val)

    def base_text(self, loc):
        """
        获取元素的文本内容
        :param loc:
        :return:
        """
        return self.base_find_element(loc).text

page/page_login.py

构建页面对象模型

# -*- coding: utf-8 -*-
# @Time : 2022/7/9 11:01
# @Author : ZhongShaoFeng
# @File: page_login.py

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


class PageLogin(Base):

    # 定义属性,元素定位
    username = (By.NAME, 'username')
    password = (By.NAME, 'password')
    submit = (By.NAME, 'submit')
    user_info = (By.NAME, 'userinfo')

    def page_login(self):
        """
        执行登录操作
        :return:
        """
        # 输入账号,密码
        self.base_input(PageLogin.username, 'admin')
        self.base_input(PageLogin.password, 'abc12345678')
        # 点击提交
        self.base_click(PageLogin.submit)

    def page_get_user_info(self):
        """
        查看登录之后,用户的信息
        :return:
        """
        return self.base_text(PageLogin.user_info)

testcase/test_case_login.py

编写测试用例

# -*- coding: utf-8 -*-
# @Time : 2022/7/9 11:07
# @Author : ZhongShaoFeng
# @File: test_case_login.py

import unittest
from page.page_login import PageLogin


class TestCaseLogin(unittest.TestCase):

    def setUpClass(self) -> None:
        """
        前置处理器,初始化页面
        :return:
        """
        url = 'http://www.baidu.com/login'
        self.login = PageLogin(url=url)

    def tearDownClass(self) -> None:
        """
        后置处理器,关闭浏览器
        :return:
        """
        self.login.driver.quit()

    def test_01_login(self):
        """
        测试:登录功能
        :return:
        """
        # 登录
        self.login.page_login()
        # 获取的用户信息
        userinfo = self.login.page_get_user_info()
        # 断言,查看用户信息标题,是否包含欢迎登录,否则则代表登录失败
        self.assertIn('欢迎登录,', userinfo)

testdata

存放一些数据,暂时不用

all.py

运行测试用例

# -*- coding: utf-8 -*-
# @Time : 2022/7/9 10:43
# @Author : ZhongShaoFeng
# @File: all.py

import unittest


suite = unittest.defaultTestLoader.discover(
    start_dir='testcase',
    pattern='test_*.py'
)
runner = unittest.TextTestRunner()
runner.run(suite)

三、数据驱动和关键字驱动

1. 数据驱动

什么是数据驱动?
数据驱动是现在主流的自动化测试设计模式之一,以数据驱动测试

数据驱动的意义:
通过不同的数据对同一个脚本实现循环测试,最终实现数据与脚本的分离,我们只需要关注数据撰写,无需关注脚本的实现
在企业实战中,可以让项目组长带队,前期构架好自动化测试框架,同时脚本编写好,以数据驱动测试,那么,其他的测试人员,只需要撰写测试用例的数据,然后,通过一键执行脚本,自动生成报告,其他测试人员无需关注脚本是如何实现的,如何编写的。

结合unittest 框架,如何实现数据驱动?
我们可以通过ddt模块实现
ddt安装:pip install ddt

DDT模块:

  1. DDT 类装饰器,装饰器继承unittest.TestCase的类
  2. data 装饰器方法,data() 可以传入列表,元祖,字典作为参数
  3. unpack 装饰器方法,unpack 可以把复杂的数据,分解为多个数据
  4. file_data 装饰器方法,file_data() 可以直接接收数据文件(json数据文件,基于yaml的数据文件)

通过ddt的数据传动,实现循环执行测试用例

@ddt.data(['admin1', 'admin2'])
    def test_01_login(self, username):
        """
        测试:登录功能
        :return:
        """
        print(username)
        # 登录
        self.login.page_login()
        # 获取的用户信息
        userinfo = self.login.page_get_user_info()
        # 断言,查看用户信息标题,是否包含欢迎登录,否则则代表登录失败
        self.assertIn('欢迎登录,', userinfo)

比如这里,我们通过ddt.data 传入了一个列表,列表有两组数据,那么,test_01_login 测试用例,就会自动执行两次

2. 关键字驱动

什么是关键字驱动?
以关键字函数驱动测试,关键字驱动又叫动作字驱动
就是把业务封装成关键字函数,再基于关键字实现自动化测试

覆盖项目业务=用例集合覆盖测试
用例集合覆盖测试=一个一个的用例实现覆盖测试
实现覆盖测试=多个操作步骤组成
多个操作步骤=多个关键字函数
(那么操作步骤就等于关键字函数)

比如:
登录用例
实际上分为多个步骤组成,打开浏览器,打开登录页面,输入账号,输入密码,再点击登录按钮

那么关键字函数就是:
open_browser() 打开浏览器
load_url() 加载登录页面
input() 输入账号
input() 输入密码
click() 点击登录按钮

这样就实现了操作步骤,也就实现了关键字函数,也就实现了通过关键字函数驱动步骤,从而去驱动测试用例的执行


更新日志

提示:将会持续更新优化

20220709,TestSuite 测试套件-添加单个测试用例,添加多个测试用例,加载类下的所有用例,加载某个路径下的所有测试用例,TestRunner 测试运行器-TextTestRunner 文本测试报告生成器,生成Html报告,装饰器@unittest.skip强制跳过,@unittest.skipIf 符合条件,则跳过,@unittest.skipUnless 符合条件,则不跳过,POM 设计模型-什么是POM 设计模型?,POM 分为四层设计架构-base 基类,page 页面对象类,testcase 测试用例层,testdata 测试数据,如何基于POM进行自动化测试框架架构?数据驱动和关键字驱动-数据驱动,关键字驱动

20220708,Unittest 框架-TestCase 测试用例,TestFixture 测试夹具

你可能感兴趣的:(python,开发语言)