python自动化测试 | unittest框架学习(一)

在这里插入图片描述


一、unittest五大组件

  • test case:测试用例
    unittest 提供一个基类 TestCase,用于新建测试用例。一个测试用例是一个独立的测试单元,unittest中测试用例方法都是以test开头的,且执行顺序会按照方法名的ASCII值排序。

  • test suite:测试套件
    多个测试用例的集合,把需要一起执行的测试用例集中放到一块执行,使用TestLoader来加载测试用例到测试套件中。

  • test fixure:测试夹具
    用于测试用例环境的搭建和销毁。测试前环境准备(setUp),执行测试代码(run),以及测试后环境还原(tearDown)。

  • test loader 测试加载器
    加载TestCase到TestSuite中的,并创建它们的实例,然后添加到TestSuite中,返回TestSuite实例。

  • test runner 测试运行器
    执行测试用例,并将测试结果保存到TextTestResult实例中。默认选择TextTestRunner(文本运行器),这个运行器可能使用图形接口、文本接口,或返回一个特定的值表示运行测试的结果。

二、TestCase用法

注:这里编译器使用的pyCharm 进行演示。

import unittest     # 导入unittext包

class StadyUnittest(unittest.TestCase): # 定义类,继承unittest.TestCase方法

    # 测试用例 1
    def test01_case1(self):
        print("测试1:case1")

    # 测试用例 2
    def test02_case2(self):
        print("测试2:case2")

    # 测试用例 3
    def test03_case3(self):
        print("测试3:case3")

注意这里无需写 main 函数也可以执行。鼠标在哪一个函数后面右击运行,即可对哪一个函数进行单元测试。如下图,选择测试 test01_case() 函数:
在这里插入图片描述
测试 test03_case3() 函数:
在这里插入图片描述
如果想执行所有测试用例,在类名处点击执行即可。
在这里插入图片描述

2.1 命令行运行TestCase
2.1.1 使用命令行执行测试单元
  1. 使用 python -m unittest stady_test.StadyUnittest 命令可以达到上述相同的执行效果。
    在这里插入图片描述
  2. 使用 -v 参数可以输出更详细的报告。另外,我们也可以只指定类中的某个测试用例执行。
    在这里插入图片描述
    或者我们也可以使用 -k 匹配测试用例选择那些测试用例去执行。其中 -k 有两种匹配方法。
    • -k*key :如果有通配符 * ,则表示以 “key” 结尾的测试单元匹配
    • -kkey : 不好通配符 * 啧表示,只有含有 “key” 就匹配
      在这里插入图片描述
2.1.2 常见的unittest命令行参数
  1. 除此之外,我们还可以使用 python -m unittest -h 获得unittest 的命令参数 。
    在这里插入图片描述
选项说明
-b, --buffer在测试运行时,标准输出流与标准错误流会被放入缓冲区。成功的测试的运行时输出会被丢弃;测试不通过时,测试运行中的输出会正常显示,错误会被加入到测试失败信息。
-c, --catch当测试正在运行时, Control-C 会等待当前测试完成,并在完成后报告已执行的测试的结果。当再次按下 Control-C 时,引发平常的 KeyboardInterrupt 异常。
-f, --failfast当出现第一个错误或者失败时,停止运行测试。
-k只运行匹配模式或子串的测试方法和类。可以多次使用这个选项,以便包含匹配子串的所有测试用例。
包含通配符(*)的模式使用 fnmatch.fnmatchcase() 对测试名称进行匹配。另外,该匹配是大小写敏感的。
模式对测试加载器导入的测试方法全名进行匹配。
例如,-k foo 可以匹配到 foo_tests.SomeTest.test_something 和 bar_tests.SomeTest.test_foo ,但是不能匹配到 bar_tests.FooTest.test_something 。
–locals在回溯中显示局部变量。
-v, --verbose更详细地输出结果。
-s, --start-directory directory开始进行搜索的目录(默认值为当前目录 . )。
-p, --pattern pattern用于匹配测试文件的模式(默认为 test*.py )。
-t, --top-level-directory directory指定项目的最上层目录(通常为开始时所在目录)。
  1. 需要知道的是,我们使用编译右键运行的方式,最终也是通过命令行方式执行的,我们运行的每一种不同的实例,在我们右键鼠标运行时,编译器自动帮我们进行了相关参数的配置。
    在这里插入图片描述
    如图,我们的stady_test.StadyUnittest.test01_case1 已经在配置中为我们生成了。
    在这里插入图片描述
2.2 使用unittest.main() 方法

在源文件中添加如下代码:

    if __name__ == '__main__':
        unittest.main()
2.2.1 配置PyCharm执行

如果想以这种方式运行程序,我们需要配置一下编译器。

  • 第一步,点击编辑配置
    在这里插入图片描述
  • 第二步,选择 + 添加
    在这里插入图片描述
  • 第三步,选择python文件
    在这里插入图片描述
  • 第四步,将文件路径填写完整。直接点击即选择目录即可。
    在这里插入图片描述
  • 第五步,配置完成后点击应用。我们就有了一个当前文件的配置。点击右上角绿色三角形即可执行。
    在这里插入图片描述
    接下来,选择好配置,直接执行即可。
    在这里插入图片描述
    上述方法等同于直接在命令行运行 python stady_test.py
    在这里插入图片描述
2.2.2 测试结果分析

测试结束在屏幕输出的文本信息中有四种类型:

形式表示类型
.成功
F失败
E错误
S跳过

下面使用如下代码,分别输出上述四种状态。

import unittest     # 导入unittext包

class StadyUnittest(unittest.TestCase): # 定义类,继承unittest.TestCase方法

    # 测试用例 1:正常执行
    def test01_case1(self):
        print("测试1:case1")

    # 测试用例 2:异常
    def test02_case2(self):
        raise Exception("测试用例2 异常")
        print("测试2:case2")

    # 测试用例 3:错误
    def test03_case3(self):
        print("测试3:case3")
        self.assertEqual(1, 2)   # 断言出错

    # 测试用例 4:跳过
    @unittest.skip("跳过case4")        # 装饰器,用于跳过此用例
    def test04_case4(self):
        print("测试4:case4")

if __name__ == '__main__':
    unittest.main()
  • pyCharm执行结果
    在这里插入图片描述
  • 命令行执行结果
    在这里插入图片描述
2.2.3 测试用例的执行顺序

unittest.main() 方法默认执行所有的测试用例,并且测试用例的执行是按照一定的顺序执行的 。

上述代码中,我们设置了四个测试单元函数,他们的函数名均以test开头,编号为 00、01、02、03 。目的就是让他们按照一定的执行顺序执行下去。

因为,单元测试用例的执行顺序就是以ascll码的顺序执行的。例如我们可以新建一个python文件,写下如下代码进行测试。

import unittest     # 导入unittext包

class StudyUnittest(unittest.TestCase): # 定义类,继承unittest.TestCase方法

    def testa_case(self):
        print("case4 : testa_...")

    def testZ_case(self):
        print("case3 : testZ_...")

    def test01_case(self):
        print("case1 : test01_...")

    def test20_case(self):
        print("case2 : test20_...")


if __name__ == '__main__':
    name = [a for a in dir(StudyUnittest) if a.startswith('test')]
    print("name: ", name)
    print("sort: ", sorted(name)) # 输出排序后的 name
    print("执行顺序:")
    unittest.main()

执行结果:按照 0 ~ 9,A ~ Z,a ~ z 的顺序。
在这里插入图片描述

2.3 分析unittest.main() 方法

unittest.main() 函数有很多参数,下面是unittest文件中 main() 的函数声明

unittest.main(
	module='__main__',          # 测试用例所在的路径,__main__表示当前模块
	defaultTest=None,           # 待测用例额的名称,默认是所有
	argv=None,                  # 接收外部的参数
	testRunner=None,            # 测试运行器,默认是文本测试运行器 TextTestRunner
	testLoader=unittest.defaultTestLoader, # 测试用例加载器
	exit=True,                  # 是否在测试完成之后退出程序
	verbosity=1,                # 表示测试结果的信息复杂度,有0、1、2 三个值.  -v
	failfast=None,              # 用例遇到失败后停止,不在执行后面的用例       -f
	catchbreak=None,            # ctrl-c 中断后执行完输出测试结果            -c
	buffer=None,                # 在测试运行期间缓冲stdout和stderr          -b
	warnings=None               # 显示警告信息
	)

以上为 unittest.main() 中的参数信息,下面简单的测试一下其中的几个参数选项:

  1. 使用 defaultTest 参数
if __name__ == '__main__':
    unittest.main(defaultTest=["StudyUnittest.test02_case"])
  1. 使用 verbosity 参数
    类似命令行参数中的 -v 选项,0,1,2分别表示输出结果的详细程度,默认为1 。
if __name__ == '__main__':
   print("\n_______________v = 0 :")
   unittest.main(verbosity=0,exit=False)
   print("\n_______________v = 1 :")
   unittest.main(verbosity=1,exit=False)
   print("\n_______________v = 2 :")
   unittest.main(verbosity=2,exit=True)
  1. 使用 argv 参数
    参数第一位是文件名: study_test3.py ,第二位是运行的模块名 StudyUnittest.test01_case
if __name__ == '__main__':
    a = unittest.main(argv=['study_test3.py', 'StudyUnittest.test01_case'])

三、TestSuite用法

3.1 使用TestSuite进行测试

使用TestSuite进行测试的步骤有三步:

  1. 使用unittest.TestSuite()生成一个套件实例
  2. 使用TestSuite的 addTest()addTests() 把测试用例加载到套件中。
    其中前者每次只加载一个测试用例,后者可一次加载多个测试用例。
  3. 调用unittest.main() 方法选择对应套件执行测试。
    除了使用unittest.main()方法执行之外,还可以使用 run() 方法。具体请看 3.2、3.3 小节。
3.1.1 使用unittest.main()方法执行套件
import unittest

class StudyUnittest(unittest.TestCase):

    def test01_case(self):
        print("test01_case")

    def test02_case(self):
        print("test02_case")

    def test03_case(self):
        print("test03_case")

if __name__ == '__main__':
    suite = unittest.TestSuite()     # 生成一个套件
    suite.addTest(StudyUnittest("test01_case"))  # 加载测试用例到套件
    suite.addTest(StudyUnittest("test02_case"))  # 加载测试用例到套件
    unittest.main(defaultTest='suite')     # 选择默认执行的测试用例为 suilt 套件

执行结果:
在这里插入图片描述

3.1.2 使用TextTestRunner().run()方法执行套件

除此之外,我们也可以使用测试运行器运行该套件。

if __name__ == '__main__':
    suite= unittest.TestSuite()     # 生成一个套件
    suite.addTest(StudyUnittest("test01_case"))  # 加载测试用例到套件
    suite.addTest(StudyUnittest("test02_case"))  # 加载测试用例到套件
    # unittest.main(defaultTest='suilt')     # 选择默认执行的测试用例为 suilt 套件
    # 使用运行器运行该套件
    unittest.TextTestRunner().run(suite)

因为调用 unittest.main() 方法,最终都将转为调用 unittest.TextTestRunner() 方法。

  • 其中 unittest.main() 具体的步骤为:
  • 编写TestCase,由TestLoader加载TestCase到TestSuite
  • 然后由TextTestRunner来运行TestSuite
  • 最后将运行的结果保存在TextTestResult中。

而在 TextTextRunner 中实际上调用的 run() 方法执行的。

由此,我们可以使用函数定义我们的测试套件,通过以下方式进行测试:

# 定义测试套件生成函数
def suite():
    suite = unittest.TestSuite()
    suite.addTest(StudyUnittest("test01_case"))  # 加载测试用例到套件
    suite.addTest(StudyUnittest("test02_case"))  # 加载测试用例到套件
    return suite

if __name__ == '__main__':
    runner = unittest.TextTestRunner()
    runner.run(suite())
3.1.3 使用TestSuite的run()方法执行套件

在这里插入图片描述

在pytthon 开发文档中显示 unittest.TestSuite() 中也存在一个 run() 方法。

  • unittest.TestSuite().run()运行与此套件关联的测试,将结果收集到作为结果传递的测试结果对象中。 请注意,与TestCase.run()不同,TestSuite.run()要求传递结果对象。

同时我们可以查看TestCase类中,也有一个 run() 方法。
在这里插入图片描述

  • 参考: unittest之TestSuite类详解 这篇文章
    测试套件(TestSuite)的执行是通过它的run方法,而它的run方法又会去调用每一个用例(TestCase)的run方法。这样就实现了用例的执行

而我们之前提到的,unittest.main()的执行最终是调用了TextTestRunner.run()方法,而TextTestRunner.run()方法又根据每个suite去调用TestSuite.run()或TestCase.run() 去执行测试的。

if __name__ == '__main__':
    #实例化一个TextTestResult类
    result = unittest.TextTestResult(sys.stdout,'test result',1)
    suite = unittest.TestSuite(map(StudyUnittest,['test01_case']))
    '''
    上面语句等同于 创建一个TestSuite实例,并向其中添加一个estCase
    suite = unittest.TestSuite()
    suite.addTest(StudyUnittest('test01_case'))
    '''
    suite.run(result)

    # 使用TestCase实例的run()方法执行
    testcase = StudyUnittest('test02_case')
    testcase.run(result)
3.3.4 使用addTests()加载测试用例集

addTests() 接收的参数是一个列表类型,函数原型:def addTests(self, tests: Iterable[_TestType]) -> None:

下面演示用法:

if __name__ == '__main__':
    suite = unittest.TestSuite()
    testcases = [                   # 测试用例集
        StudyUnittest("test01_case"),
        StudyUnittest("test02_case")
    ]
    suite.addTests( testcases)    # 加载测试用例集
    unittest.main(defaultTest='suite')

四、TestLoader用法

我们发现使用 addTests() 与使用 addTest() 加载测试用例都太过麻烦,那么我们可以使用测试加载器进行测试用例的加载。

4.1 使用discover()方法加载测试用例

我们使用TestLoader 类中提供的discover()方法。 discover(start_dir,pattern='test*.py',top_level_dir= None)

  • 该方法可以找到指定目录下所有测试模块,并可递归查到子目录下的测试模块,只有匹配到文件名时才加载
  • start_dir:要测试的模块名或测试用例目录
  • pattern=‘test*.py’:表示用例文件名的匹配原则。此处匹配以“test”开头的.py 类型的文件,* 表示任意多个字符
  • top_level_dir= None:测试模块的顶层目录,如果没有顶层目录,默认为None
import os
import unittest

class StudyUnittest(unittest.TestCase):

    def test01_case(self):
        print("test01_case")

    def test02_case(self):
        print("test02_case")

    def test03_case(self):
        print("test03_case")

if __name__ == '__main__':
    suite = unittest.TestSuite()
    testcases = unittest.defaultTestLoader.discover(
        start_dir=os.getcwd(),  # 目录:os.getcwd()当前目录路径
        pattern='*.py'          # 文件: 所有以 .py 结尾的文件
    )
    suite.addTests( testcases)    # 加载测试用例集
    unittest.main(defaultTest='suite')

测试结果:
在这里插入图片描述

4.1 unittest.TestLoader提供的方法

除了上述示例中的discover()方法,TestLoader还提供了其他方法,下面将列出一部分内容仅供参考。

TestLoader类用于从类和模块创建测试套件。通常,不需要创建该类的实例;unittest模块提供了一个可以作为unittest. defaulttestloader共享的实例。但是,使用子类或实例允许自定义一些可配置属性。

loadTestsFromTestCase(testCaseClass)
  • 返回由TestCase派生的testCaseClass中包含的所有测试用例的套件。

  • 将为每个由getTestCaseNames()命名的方法创建一个测试用例实例。 默认情况下,这些是以test开头的方法名称。 如果getTestCaseNames()不返回任何方法,但实现了runTest()方法,则将为该方法创建一个测试用例。

loadTestsFromModule(module, pattern=None)
  • 返回给定模块中包含的所有测试用例的套件。 此方法在模块中搜索从TestCase派生的类,并为为该类定义的每个测试方法创建该类的实例。
loadTestsFromName(name, module=None)
  • 给定一个字符串说明符,返回所有测试用例的套件。

  • 指定者名称是“点名”,可以解析为模块,测试用例类,测试用例类中的测试方法,TestSuite实例或返回TestCase或TestSuite实例的可调用对象。 这些检查按照此处列出的顺序进行; 也就是说,将在可能的测试用例类上的方法选择为“测试用例类内的测试方法”,而不是“可调用对象”。

getTestCaseNames(testCaseClass)
  • 返回在testCaseClass中找到的方法名称的排序序列; 这应该是TestCase的子类。
discover(start_dir, pattern=‘test*.py’, top_level_dir=None)
  • 通过从指定的起始目录递归到子目录中,找到所有测试模块,然后返回包含它们的TestSuite对象。 仅加载与模式匹配的测试文件。 (使用外壳样式模式匹配。)仅加载可导入的模块名称(即有效的Python标识符)。
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我叫RT

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值