基于Python的selenium入门超详细教程(第2章)--单元测试框架unittest

学习路线

自动化测试介绍及学习路线-CSDN博客

​自动化测试之Web自动化(基于python+selenium)-CSDN博客

基于Python的selenium入门超详细教程(第1章)--WebDriver API篇-CSDN博客

目录

前言:

一、单元测试

[1. 单元测试的定义](#1. 单元测试的定义)

[2. 单元测试框架](#2. 单元测试框架)

[3. 单元测试的作用](#3. 单元测试的作用)

二、unittest单元测试框架

[1. 简介](#1. 简介)

[2. unittest组成](#2. unittest组成)

[3. 详解](#3. 详解)

[3.1 TestCase](#3.1 TestCase)

步骤

[3.2 TestSuite](#3.2 TestSuite)

步骤

[3.3 TestLoader](#3.3 TestLoader)

步骤

总结:TestSuite与TestLoader区别

[3.4 TestRunner](#3.4 TestRunner)

[3.5 TestFixture](#3.5 TestFixture)

方法级别

类级别

[3.6 TestReport](#3.6 TestReport)

★三、断言

四、参数化

[安装parameterized 库](#安装parameterized 库)

步骤

五、跳过

常见问题


前言:

在之前的学习中,Selenium 与 Python 的结合已经成为了一个强大的工具集,它允许我们模拟用户在浏览器中的各种操作,如点击按钮、输入文本、切换页面等,从而实现对 Web 应用程序的自动化测试。然而,仅仅能够执行这些操作是不够的,我们还需要一种有效的方式来组织和管理这些测试用例,确保测试的可靠性和可维护性。

unittest作为 Python 内置的单元测试框架,就提供了这样一种解决方案。它借鉴了其他流行测试框架(如 JUnit、NUnit)的设计理念,为 Python 开发者提供了一套完整的测试体系,包括测试用例的定义、测试套件的组织、测试运行器的执行以及测试结果的反馈等功能。

当我们将 Selenium与unittest结合使用时,能够充分发挥两者的优势。Selenium 负责模拟用户在浏览器中的实际操作,而unittest则负责对这些操作的结果进行验证和管理。通过这种方式,我们可以编写结构化、可重复执行的自动化测试用例,确保 Web 应用程序在不同的场景下都能正常工作。

一、单元测试

想要学明白unittest测试框架,首先我们得了解单元测试的含义

1. 单元测试的定义

软件测试的类型按阶段划分为单元测试,集成测试,系统测试,验收测试。其中单元测试又称模块测试,针对软件设计中的最小单位-程序模块,进行正确性检查的测试工作。单元测试需要从程序内部结构出发设计测试用例。多个模块可以平行地独立进行单元测试。通常是在程序员编写代码时就编写的,用于验证代码的每个部分是否符合预期功能。它有助于发现和修复代码中的错误,提高代码的质量和可靠性,一般由相关开发人员进行

其中单元测试中单元的含义:

单元就是人为规定的最小的被测功能模块,如C语言中单元指一个函数,Java里单元指一个类,图形化的软件中可以指一个窗口或一个菜单等。在实际项目中,单元测试往往由开发人员完成。

2. 单元测试框架

单元测试框架是用来支持编写和执行单元测试的工具。它提供了一些功能和工具,比如断言(assertions)、测试运行器(test runners)、测试夹具(test fixtures)等,来帮助程序员编写和管理测试用例。

  • 单元测试其实就是构造数据使用一段代码去测试另一段代码,理论上来说,不使用单元测试框架也能进行单元测试。但如果用于单元测试的代码(即测试用例)增多,在没有测试框架的情况下会变得拥挤、不可管理,这个时候引入测试框架就变得尤为重要。
  • 单元测试框架提供了一种统一的编程模型,可以将测试定义为一些简单的类,这些类中的方法可以调用希望测试的应用程序代码。利用单元测试框架,可以很轻松地插入、设置和分解有关测试的功能,可以直观方便地管理测试用例。
  • 主流的单元测试框架,如Java的Junit、TestNg,python的Unittest、Pyunit、Pytest,通用的自动化测试框架Robot Framework等。

3. 单元测试的作用

根据默认测试用例的规则找到测试用例--->执行测试用例--->判断测试用例的结果--->生成测试报告

  • 提供用例组织与执行,能够组织多个用例执行,提高了测试效率和可维护性
  • 提供丰富的断言方法,让程序代码代替人工自动的判断预期和实际结果是否相符
  • 提供丰富的日志与测试结果记录,保证了测试的一致性

主要的 Python 单元测试框架包括 unittestpytest,本文聚焦于学习unittest框架的使用。

补充:Web自动化用unittest,接口自动化用pytest

二、unittest单元测试框架

参看课程:2023最新UnitTest自动化测试框架和unittest数据驱动实战讲解_哔哩哔哩_bilibili

本文在之前介绍过的WebDeiver API用法的基础上使用unittest框架进行WEB自动化测试。使用WebDriver、Unittest组件,使用元素定位方式,完成测试用例,输出测试报告的简单步骤

1. 简介

unittest是python内置的一种单元测试框架,它受到了 JUnit、NUnit 等知名测试框架的启发,为 Python 开发者提供了一套强大且完善的单元测试解决方案,还可以适用于WEB自动化测试用例的开发与执行。该测试框架可组织执行测试用例,并且提供了丰富的断言方法,判断测试用例是否通过,最终生成测试结果。

对于测试人员来说,unittest框架充当自动化脚本(用例代码)的执行框架,用来管理运行和监控多个测试用例

2. unittest组成

  • TestCase测试用例(最核心):代表一个测试用例Test Case,unittest中执行测试的最小单元
  • TestSuite测试套件:整理测试用例TestCase,将多个TestCase对象打包形成一个集合,以便一次性运行多个相关的测试用例
  • TestLoader测试加载器:加载测试,对测试套件TestSuiter功能的补充,用来整理打包测试用例TestCase
  • TestRunner测试运行器:执行测试,用来实际运行测试套件TestSuite,执行测试方法,并收集和报告测试结果。
  • TestFixture测试夹具:执行测试用例前的前置操作及后置操作
  • TestReport测试报告:报告测试

3. 详解

提醒:文件名只能由数字、字母、下划线构成,且不能用数字开头,否则会报错

3.1 TestCase

Unittest提供testCase类来编写测试用例,一个TestCase的实例就是一个测试用例。一条测试用例就是一个完整的测试流程,包括测试前准备环境的搭建(setUp),执行测试代码(run),以及测试后环境的还原(tearDown),通过运行一条测试用例,可以对某一个问题进行验证。

unittest中TestCase部分的规则:

​ 1、测试文件必须导包:import unittest

​ 2、测试类必须继承 unittest.TestCase:class testApi(unittest.TestCase)

​ 3、测试用例必须以 test_开头

主要作用:每个TestCase都是一个代码块,包含真正的用例代码

用例执行顺序:

按照ASCII 0-9 a-z A-Z 顺序来执行用例

命令行命令:

执行类下的所有用例 python -m unittest 用例文件名.用例类

执行某一条用例: python -m unittest 用例文件名.用例类.用例名称

步骤
  1. 导包 import unittest
  2. 自定义测试类,继承unittest.TestCase
  3. 添加测试固件,setUp()、tearDown()函数;setUpClass()、tearDownClass() 函数
  4. 定义测试方法,即在测试类中书写测试用例 必须以test_开头,并设置断言
  5. 执行用例,默认方式为调用unittest.main()方法,执行当前模块中的所有测试用例
python 复制代码
import unittest

#封装测试用例
class LoginTest(unittest.TestCase):
    #testerFixture
    @classmethod
    def setUpClass(cls):  #注意,这里需要使用装饰器@classmethod
        pass

    @classmethod
    def tearDownClass(cls):  #注意,这里需要使用装饰器@classmethod
        pass

    def setUp(self): 
        print('打开浏览器')  #这里的print均是模拟测试用例

    def tearDown(self):
        print('关闭浏览器')

    def test_admin(self):
        print('admin用户登录系统')
        print('admin用户退出系统')

    def test_user(self):
        print('user用户登录系统')
        print('user用户退出系统')

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

'''
Ran 2 tests in 0.002s

OK
打开浏览器
admin用户登录系统
admin用户退出系统
关闭浏览器
打开浏览器
user用户登录系统
user用户退出系统
关闭浏览器
'''

3.2 TestSuite

一个测试套件是多个测试或测试用例的集合,是针对被测程序的对应的功能和模块创建的一组测试,一个测试套件内的测试用例将一起执行

步骤
    1. 导包 import unittest
    1. 实例化(创建对象)套件对象 suite=unittest.TestSuite() #实例化一个suite
    1. 使用套件对象添加测试用例方法

3.1 法一:通过套件对象.addTest() 往测试套件里添加单个测试用例,或通过套件对象.addTests([...]) 添加多个测试用例(列表中为用例方法名)

3.2 法二:通过套件对象.addTest(unittest.makeSuite(测试类名))将一个测试类中的所有测试方法进行添加 注:在 Python 3.11 及之后的版本中,unittest.makeSuite 方法已被弃用

    1. 实例化运行对象,并返回测试结果 runner=unittest.TextTestRunner()
    1. 使用运行对象去执行套件对象 运行对象.run(套件对象)
python 复制代码
# 1.导报
import unittest

from webscript_3_15 import LoginTest

# 2.实例化套件对象
suite = unittest.TestSuite() 
# 3.添加用例方法
#法一:使用套件对象.addTest()添加用例方法
# suite.addTests([LoginTest('test_1'), LoginTest('test_admin')])
#法二:使用套件对象.addTest(unittest.makeSuite(测试类名))添加
#suite.addTest(unittest.makeSuite(LoginTest))

# 4. 实例化运行对象,并返回结果
runner = unittest.TextTestRunner()  
# 5. 使用运行对象.run(套件对象)去执行套件对象
runner.run(suite)
'''
打开浏览器
第一个测试用例
关闭浏览器
打开浏览器
第二个测试用例
关闭浏览器
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK
'''

3.3 TestLoader

用于自动发现和加载测试用例。它可以从模块、类或目录中收集测试用例,并将符合条件的测试方法添加到测试套件中,与Testsuite功能一样,对他功能的补充,用来组装测试用例

写法:

  1. suite = unittest.TestLoader().discover("指定搜索的目录文件","指定字母开头模块文件")

  2. suite = unittest.defaultTestLoader.discover("指定搜索的目录文件","指定字母开头模块文件") 【推荐】

注意:

如果使用写法1,TestLoader()必须有括号。

步骤
    1. 导包 import unittest
    1. 实例化(创建对象)套件对象并添加测试用例 suite=unittest.TestLoader().discover() #实例化一个suite

2.1 法一:通过unittest.TestLoader()创建加载对象,加载测试用例类 (属于TestLoader)

2.2 法二:批量通过unittest.defaultTestLoader.discover()将指定路径的测试用例加载至测试用例集**(属于TestLoader)**

    1. 实例化运行对象,并返回测试结果 runner=unittest.TextTestRunner()
    1. 使用运行对象去执行套件对象 运行对象.run(套件对象)
python 复制代码
# 1.导报
import unittest

from webscript_3_15 import LoginTest

# 2.实例化套件对象并添加用例方法
#法二: 批量将指定路径的测试用例加载至测试用例集,这里将开头是test的用例文件全部载入
suite=unittest.defaultTestLoader.discover(start_dir='./test_demo', pattern='test_page2.py')

# 3. 实例化运行对象,并返回结果
runner = unittest.TextTestRunner()  
# 4. 使用运行对象.run(套件对象)去执行套件对象
runner.run(suite)
'''
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK
打开浏览器
第一个测试用例
关闭浏览器
打开浏览器
第二个测试用例
关闭浏览器
'''

总结:TestSuite与TestLoader区别

共同点:都是测试套件

不同点:实现方式不同

TestSuite: 要么添加指定的测试类中所有test开头的方法,要么添加指定测试类中指定某个test开头的方法

TestLoader: 搜索指定目录下指定字母开头的模块文件中以test字母开头的方法并将这些方法添加到测试套件中,最后返回测试套件

3.4 TestRunner

即测试执行器,负责测试执行调度并且生成测试结果给用户,该模块中提供run方法执行TestSuite中的测试用例,并使用图形界面、文本界面或者特定的返回值来返回测试用例的执行结果,如运行的用例总数、用例通过数、用例失败数

其中:点"."表示通过,"F"表示失败。

3.5 TestFixture

即测试夹具(固件),用于测试用例环境的搭建和销毁。定义在单个或多个测试执行之前的准备工作和测试执行之后的清理工作,在测试步骤执行前需要为该测试用例准备环境(SetUp、SetUpClass),如启动app或打开浏览器,测试步骤执行后需要恢复环境 (TearDown、TearDownClass),如关闭app或浏览器,这时候就需要用到Fixture,使代码更简洁

书写在测试用例TestCase中,是一个代码结构,每个测试用例中重复的代码就可以写在Fixture夹具中,只写一遍,但每次测试用例方法的执行都会执行Fixture中的代码

举例:

前置操作:1.登录浏览器 2.输入网址

中间操作: 测试方法1

测试方法2

后置操作:1.关闭浏览器

方法级别

在每个测试方法(用例代码)执行前后都会自动调用的结构;几个测试函数,执行几次

  • setUp:在每个测试方法执行前调用,用于为单个测试方法准备环境。(初始化)
  • tearDown:在每个测试方法执行后调用,用于清理单个测试方法使用的资源。(释放)

场景:当你要登录自己的用户名账户的时候,都会输入网址,当你准备不用这个页面了,都会关闭当前页面;

1、输入网址 (方法级别)

2、关闭当前页面 (方法级别)

类级别

在每个测试类中所有方法执行前后 都会自动调用的结构;整个类只运行一次,必须用@classmethod修饰

  • setUpClass:在测试类中的所有测试方法执行前调用一次,用于为整个测试类准备共享资源。
  • tearDownClass:在测试类中的所有测试方法执行后调用一次,用于清理整个测试类使用的共享资源。

场景:你上网的整个过程都首先需要打开浏览器,关闭浏览器,而他们整个过程都需要执行一次,那么就可以用类级别。

提示:

无论使用函数级别还是类级别,最后常用场景为:

初始化:

  1. 获取浏览器实例化对象

  2. 最大化浏览器

  3. 隐式等待

结束:

关闭浏览器驱动对象

3.6 TestReport

测试报告用于直观地展示所有执行用例的成功或者失败状态的汇总,包括失败的测试步骤的预期结果和实际结果,还有整体运行状况和运行时间的汇总,帮助开发人员和测试人员快速了解测试的情况。unittest 本身没有内置强大的测试报告生成功能,但可以结合第三方库如 HTMLTestRunner 、beautifulReport来生成美观的 HTML 测试报告。

BeautifulReport:

python 复制代码
##部分代码
from BeautifulReport import BeautifulReport

#原先代码
# runner = unittest.TextTestRunner()  # 实例化运行对象,并返回结果
# runner.run(suite)  # 使用运行对象.run(套件对象)去执行套件对象

#替换为beautifulReport
result = BeautifulReport(suite)
result.report(description='系统登录的测试报告',filename='report.html',report_dir='./report')

★三、断言

在Python 的unittest单元测试框架中,断言(Assertion)是非常关键的部分,它用于验证测试过程中的实际结果是否与预期结果一致,让程序代替人工自动的判断。如果实际结果和预期结果不相符,断言就会抛出 AssertionError 异常,从而表明测试用例执行失败,只有设置正确合适的断言才能获取正确的测试结果

断言的结果:

>True,用例通过

>False,代码抛出异常,用例不通过

在unittest中使用断言,需要通过 self.断言方法,

Unittest框架提供了自己的断言方法,如下:

断言方法 判断内容
assertEqual(expected, actual) 判断 expected == actual ,不等则fail
assertNotEqual(a, b) 判断 a != b
assertTrue(x) 判断 bool(x) is True
assertFalse(x) 判断 bool(x) is False
assertIs(a, b) 判断a和b是否为(is)同一个对象,即它们是否指向内存中的同一个地址
assertIsNot(a, b) 判断a和b是否不是同一个对象,即它们的内存地址不同
assertIsNone(x) 判断 x is None
assertIsNotNone(x) 判断 x is not None
assertIn(member, container) 判断member是否在container中。container通常是一个可迭代对象,如列表、元组、字符串等
assertNotIn(a, b) 判断 a not in b
assertIsInstance(a, b) 判断a是否是b类型的实例。b可以是一个类、类型对象或一个由类和类型对象组成的元组
assertNotIsInstance(a, b) 判断 not isinstance(a, b)
python 复制代码
import unittest

from tools import login #login为待测试的方法模块

class TestLogin(unittest.Testcase):
    def test_username_password_ok(self):
        """正确的用户名和密码:admin,123456,登录成功"""
        self.assertEqual("登录成功", login('admin','123456'))

    def test_username_error(self):
        """错误的用户名:root,123456,登录失败"""
        self.assertEqual("登录失败",login('root','123456'))

    def test_password_error(self):
        """错误的密码:admin,123123,登录失败"""
    self.assertEqual("登录失败",login('admin','123123'))

    def test_username_password_error(self):
        """错误的用户名和错误的密码:aaa,123123,登录失败"""
        #self.assertEqual('登录失败', login('aaa','123123'))
        self.assertIn("失败",login('aaa','123123))

四、参数化

在测试方法中,使用变量来代替具体的测试数据,这就是所谓的参数化。参数化操作将测试数据和测试逻辑分离,让同一套测试代码可以使用不同的数据进行多次运行(传参给变量),验证测试逻辑在不同输入下的测试结果。

工作中常见的实际场景:

  1. 测试数据一般保存在 json文件中

  2. 使用代码读取json文件,提取相应的数据---->元组或列表的形式

安装parameterized

unittest 框架中本身没有直接提供参数化的功能,需要借助第三方库 parameterized 来实现

步骤

  1. 导包 unittest/parameterized
  2. 自定义测试类,继承unittest.TestCase
  3. 可省略\] 添加测试固件,setUp()、tearDown()函数;setUpClass()、tearDownClass() 函数

  4. 组织测试数据并传参
python 复制代码
#1.导包
import unittest
from parameterized import parameterized
from tools import login
# 组织测试数据 [(),(),()]or[[],[],[]]
data =[
    ('admin','123456','登录成功'),  #数据顺序与参数列表保持一直
    ('root','123456','登录失败'),
    ('admin','12123','登录失败')
]
#2.定义测试类
class TestLogin(unittest.TestCase):
#3.书写测试方法(用到的测试数据使用变量代替)
#4.组织测试数据并传参(装饰器 @)
    @parameterized.expand(data)
    def test_login(self,username,password,expect):
        self.assertEqual(expect,login(username,password))

五、跳过

常见问题

代码文件命名不规范,导致运行报错

1.代码文件的名字以数字开头

2.代码文件名字中有空格

3.代码文件名字有中文

4.其他的特殊符号

(数字,字母,下划线组成,不能以数字开头)

代码运行无结果

右键运行没有 unittests for 的提示,出现的问题解决方案:

方案1:重新新建一个代码文件,将写好的代码复制进去

方案2:删除已有的运行方法

使用beautifulReport时出现没有"distutils"的情况

方案1:切换或者装一个低版本的可以解决

方案2:pip install setuptools

setuptools 是一个功能更强大的替代 distutils 的库,许多依赖 distutils 的库可以通过 setuptools 来正常工作。

相关推荐
啥都鼓捣的小yao1 小时前
Python手写机器学习的“线性回归”算法
python·算法·机器学习
大霸王龙1 小时前
PyTorch中,将`DataLoader`加载的数据高效传输到GPU
人工智能·pytorch·python
阿_旭1 小时前
基于深度学习的蛀牙智能检测与语音提示系统【python源码+Pyqt5界面+数据集+训练代码】
人工智能·python·深度学习·毕业设计·蛀牙检测
冷琴19962 小时前
基于python+django+vue.js开发的社区养老管理系统源码+运行步骤
vue.js·python·django
one day3213 小时前
Selenium 自动化测试学习总结
学习·selenium·测试工具
FL16238631293 小时前
使用yolov8+flask实现精美登录界面+图片视频摄像头检测系统
python·yolo·flask
计算机软件程序设计3 小时前
Django中的查询条件封装总结
后端·python·django
咖啡调调。3 小时前
Django连接MySQL
python·mysql·django
木易:_/4 小时前
【004】deepseek本地化部署后,python的调用方式_#py
开发语言·python