首页 > 其他分享 >上手unittest读这篇文章就够了

上手unittest读这篇文章就够了

时间:2023-09-27 11:32:21浏览次数:32  
标签:TestMathFunc __ divide unittest 就够 上手 test self


引言

本文主要介绍了如下内容

单元测试的定义;

python中如何写基础的unittest单元测试;

详解unittest中的基础知识点:断言、测试固件、suite、如何控制用例执行顺序、如何把测试结果输出到文件;

详解unittest中的高级知识点:@unittest.skip、@unittest.expectedFailure、failfast、参数化;


什么是单元测试

单元测试是用来对一个模块、一个函数或者一个类来进行正确性检验的测试工作。

比如对于函数abs(),我们可以编写的测试用例为:

(1)输入正数,比如1、1.2、0.99,期待返回值与输入相同

(2)输入复数,比如-1、-1.2、-0.99,期待返回值与输入相反

(3)输入0,期待返回0

(4)输入非数值类型,比如None、[]、{}、期待抛出TypeError

把上面这些测试用例放到一个测试模块里,就是一个完整的单元测试。

unittest工作原理

unittest中最核心的四部分是:TestCase,TestSuite,TestRunner,TestFixture

(1)一个TestCase的实例就是一个测试用例。测试用例就是指一个完整的测试流程,包括测试前准备环境的搭建(setUp),执行测试代码(run),以及测试后环境的还原(tearDown)。元测试(unit test)的本质也就在这里,一个测试用例是一个完整的测试单元,通过运行这个测试单元,可以对某一个问题进行验证。

(2)而多个测试用例集合在一起,就是TestSuite,而且TestSuite也可以嵌套TestSuite。

(3)TestLoader是用来加载TestCase到TestSuite中的。

(4)TextTestRunner是来执行测试用例的,其中的run(test)会执行TestSuite/TestCase中的run(result)方法

(5)测试的结果会保存到TextTestResult实例中,包括运行了多少测试用例,成功了多少,失败了多少等信息。

综上,整个流程就是首先要写好TestCase,然后由TestLoader加载TestCase到TestSuite,然后由TextTestRunner来运行TestSuite,运行的结果保存在TextTestResult中,整个过程集成在unittest.main模块中。核心类图关系如下:

 

上手unittest读这篇文章就够了_python

unittest的基本使用方法

1)import unittest

2)定义一个继承自unittest.TestCase的测试用例类

3)定义setUp和tearDown,在每个测试用例前后做一些辅助工作。

4)定义测试用例,名字以test开头

5)一个测试用例应该只测试一个方面,测试目的和测试内容应很明确。主要是调用assertEqual、assertRaises等断言方法判断程序执行结果和预期值是否相符。

6)调用unittest.main()启动测试

下面举个实例,来看看unittest如何测试一个简单的函数

测一个简单的加减乘除接口

mathfunc.py文件代码如下:

def add(a, b):

    return a + b

def minus(a, b):

    return a - b

def multi(a, b):

    return a * b

def divide(a, b):

    return a / b

 test_mathfunc.py文件代码如下:

import unittest

 from mathfunc import *

class TestMathFunc (unittest.TestCase):

    def test_add(self):

        self.assertEqual (3, add (1, 2))

    def test_minus(self):

        self.assertEqual (1, minus (3, 2))

    def test_multi(self):

        self.assertEqual (6, multi (3, 2))

    def test_divide(self):

        self.assertEqual (2, divide (6, 2))

if __name__ == '__main__':

    unittest.main ()

 

在pycharm中运行该用例:

 

上手unittest读这篇文章就够了_测试用例_02

输出:

 

上手unittest读这篇文章就够了_unittest_03

 

可以看到一共运行了4个测试,失败了1个,并且给出了失败原因,2 != 3.0,也就是说我们的divide方法是有问题的。上图的左侧显示了用例的运行状态,右侧显示了具体的用例运行信息。

备注:在unitest.main()中加verbosity参数可以控制输出的错误报告的详细程度,用命令行的方式运行脚本会发现区别。

0 (静默模式): 你只能获得总的测试用例数和总的结果 ;

1 (默认模式): 非常类似静默模式 只是在每个成功的用例前面有个“.” 每个失败的用例前面有个 “F” ;

2 (详细模式):测试结果会显示每个测试用例的所有相关的信息;

设置unitest.main(verbosity=2),在cmd中运行 python test_mathfunc.py,运行结果如下:

test_add (__main__.TestMathFunc) ... ok

test_divide (__main__.TestMathFunc) ... FAIL

test_minus (__main__.TestMathFunc) ... ok

test_multi (__main__.TestMathFunc) ... ok

 

======================================================================

FAIL: test_divide (__main__.TestMathFunc)

----------------------------------------------------------------------

Traceback (most recent call last):

  File "unittest_demo.py", line 32, in test_divide

    self.assertEqual (2, divide (6, 2))

AssertionError: 2 != 3.0

 

----------------------------------------------------------------------

Ran 4 tests in 0.003s

 

断言

在测试用例中,执行完测试用例后,最后一步是判断测试结果是pass还是fail,自动化测试脚本里面一般把这种生成测试结果的方法称为断言(assert)。

Python中的断言类型丰富,主要包括:

基础断言

 

上手unittest读这篇文章就够了_unittest_04

exceptions, warnings, 日志信息断言

 

上手unittest读这篇文章就够了_python_05

特殊断言

 

上手unittest读这篇文章就够了_python_06

集合断言

 

上手unittest读这篇文章就够了_测试用例_07

组织TestSuite

上面的测试用例在执行的时候没有按照顺序执行,如果想要让用例按照你设置的顺序执行就用到了TestSuite。我们添加到TestSuite中的case是会按照添加的顺序执行的。现在我们只有一个测试文件,如果有多个测试文件,也可以用TestSuite组织起来。继续上面加减乘除的例子,现在再新建一个文件,test_suite.py,代码如下:

    import unittest

    from test_mathfunc import TestMathFunc

    if __name__ == '__main__':

        suite = unittest.TestSuite()

        tests = [TestMathFunc("test_add"), TestMathFunc("test_minus"), TestMathFunc("test_divide")]

        suite.addTests(tests)

        runner = unittest.TextTestRunner(verbosity=2)

        runner.run(suite)

执行结果如下:

    test_add (test_mathfunc.TestMathFunc) ... ok

    test_minus (test_mathfunc.TestMathFunc) ... ok

    test_divide (test_mathfunc.TestMathFunc) ... FAIL

 

    ======================================================================

    FAIL: test_divide (test_mathfunc.TestMathFunc)

    ----------------------------------------------------------------------

    Traceback (most recent call last):

      File "D:\pythonWorkspace\HTMLTest\test_mathfunc.py", line 20, in test_divide

        self.assertEqual(2, divide(6, 2))

    AssertionError: 2 != 3.0

 

    ----------------------------------------------------------------------

    Ran 3 tests in 0.000s

 

FAILED (failures=1)

当测试用例达到上百条时,上述加载用例的方法效率就变得很差了,unittest提供了discover方法来解决这一问题,该方法可以自动识别测试用例,具体用法如下:

discover(start_dir,pattern='test*.py',top_level_dir= None)

找到指定目录下所有测试模块,并可递归查到子目录下的测试模块,只有匹配到文件名时才加载

start_dir:要测试的模块名或测试用例目录

pattern='case*.py':表示用例文件名的匹配原则。此处匹配以“case”开头的.py 类型的文件,* 表示任意多个字符

top_level_dir= None 测试模块的顶层目录,如果没有顶层目录,默认为None

实例代码:

test_dir='./' #表示当前目录

discover=unittest.defaultTestLoader.discover(test_dir,pattern='case*.py')

runner = unittest.TextTestRunner()

runner.run(discover)

suite相关方法

loadTestsFromTestCase(testCaseClass)

testCaseClass必须是TestCase的子类(或孙类也行)

loadTestsFromModule(module, pattern=None)

test case所在的module

loadTestsFromName(name, module=None)

name是一个string,string需要是是这种格式的“module.class.method”

loadTestsFromNames(name, module=None)

names是一个list,用法与上同

discover(start_dir, pattern=’test*.py’, top_level_dir=None)

从python文件中获取test cases

用例顺序执行

unittest在执行用例(test_xxx)时,并不是按从上到下的顺序执行,有特定的顺序。unittest框架默认根据ACSII码的顺序加载测试用例,数字与字母的顺序为:0~9,A~Z,a~z。

对于类来说,class TestAxx 会优先于class TestBxx被执行。

对于方法来说,test_aaa()方法会有优先于test_bbb()被执行。

所以我们设计用例时,如果各个用例有执行先后的顺序关系,可以参考如下:

def test_1_***

def test_2_***

def test_n_***

将结果输出到文件

现在我们的测试结果只能输出到控制台,现在我们想将结果输出到文件中以便后续可以查看。

将test_suite.py进行一点修改,代码如下: 

    import unittest

    from test_mathfunc import TestMathFunc

    if __name__ == '__main__':

        suite = unittest.TestSuite()

        tests = [TestMathFunc("test_add"), TestMathFunc("test_minus"), TestMathFunc("test_divide")]

        suite.addTests(tests)

        with open('UnittestTextReport.txt', 'a') as  f:

            runner = unittest.TextTestRunner(stream=f, verbosity=2)

            runner.run(suite)

 

运行该文件,就会发现目录下生成了'UnittestTextReport.txt,所有的执行报告均输出到了此文件中。

测试固件——testfixture

当遇到要启动一个数据库这种情况时,只想在开始时连接上数据库,在结束时关闭连接。那么可以使用setUp和tearDown函数。

    class TestDict(unittest.TestCase):

        def setUp(self):

            print ('setUp...')

        def tearDown(self):

            print ('tearDown...')

 

这两个方法在每个测试方法执行前以及执行后执行一次,setUp用来为测试准备环境,tearDown用来清理环境,以备后续的测试。

如果想要在所有case执行之前准备一次环境,并在所有case执行结束之后再清理环境,我们可以用setUpClass()与tearDownClass(),代码格式如下:

    class TestMathFunc(unittest.TestCase):

        @classmethod

        def setUpClass(cls):

            createConnection()

        @classmethod

        def tearDownClass(cls):

          closeConnection()

同理,unittest也提供了模块的fixture

def setUpModule():

    createConnection()

def tearDownModule():

    closeConnection()

 

跳过某个case——@unittest.skip

unittest提供了几种方法可以跳过case

(1)skip装饰器

代码如下

    import unittest

    from mathfunc import *

    class TestMathFunc(unittest.TestCase):

 

         .....

 

        @unittest.skip("i don't want to run this case.")

        def test_minus(self):

            self.assertEqual(1, minus(3, 2))

输出:

    test_add (test_mathfunc.TestMathFunc) ... ok

    test_minus (test_mathfunc.TestMathFunc) ... skipped "i don't want to run this case."

    test_divide (test_mathfunc.TestMathFunc) ... FAIL

 

    ======================================================================

    FAIL: test_divide (test_mathfunc.TestMathFunc)

    ----------------------------------------------------------------------

    Traceback (most recent call last):

      File "D:\pythonWorkspace\HTMLTest\test_mathfunc.py", line 28, in test_divide

        self.assertEqual(2, divide(6, 2))

    AssertionError: : 2 != 3.0

 

    ----------------------------------------------------------------------

    Ran 3 tests in 0.000s

 

    FAILED (failures=1, skipped=1)

 

skip装饰器一共有三个

unittest,skip(reason):无条件跳过

unittest.skipIf(condition, reason):当condition为True时跳过

unittest.skipUnless(condition, reason):当condition为False时跳过

(2)TestCase.skipTest()方法

    class TestMathFunc(unittest.TestCase):

    ...

    def test_minus(self):

            self.skipTest('do not run this.')

            self.assertEqual(1, minus(3, 2))

输出:

    test_add (test_mathfunc.TestMathFunc) ... ok

    test_minus (test_mathfunc.TestMathFunc) ... skipped 'do not run this.'

    test_divide (test_mathfunc.TestMathFunc) ... FAIL

 

    ======================================================================

    FAIL: test_divide (test_mathfunc.TestMathFunc)

    ----------------------------------------------------------------------

    Traceback (most recent call last):

      File "D:\pythonWorkspace\HTMLTest\test_mathfunc.py", line 20, in test_divide

        self.assertEqual(2, divide(6, 2))

    AssertionError: 2 != 3.0

 

    ----------------------------------------------------------------------

    Ran 3 tests in 0.000s

 

FAILED (failures=1, skipped=1)

预期失败—— @unittest.expectedFailure

@unittest.expectedFailure标记用例为预期是失败的,如果用例执行失败,认为是success;如果用例执行成功,则认为是failure。

例如代码:

import unittest

class ExpectedFailureTestCase(unittest.TestCase):   

@unittest.expectedFailure   

def test_fail1(self):       

      self.assertEqual(1, 0,)   

@unittest.expectedFailure   

def test_fail2(self):       

      self.assertEqual (1, 1)

@unittest.expectedFailure

def test_fail3(self):       

    self.assertEqual (1, 1)

短路测试—— failfast

failfast是TestResult的一个属性,缺省为False。 如果failfast为True,一旦测试集中有测试案例failed或发生error立即终止当前整个测试执行,跳过剩下所有测试用例。

实现“短路测试”,设置failfast为True,具体操作如下:

unittest.main(failfast=True)

unittest.TextTestRunner(failfast=True)

实例如下:

import unittest

class TestMathFunc (unittest.TestCase):

    def test_add(self):

        self.assertEqual (3, add (1, 2))

    def test_minus(self):

        self.assertEqual (1, minus (3, 2))

    def test_multi(self):

        self.assertEqual (6, multi (3, 2))

    def test_divide(self):

        self.assertEqual (2, divide (6, 2))

if __name__ == '__main__':

    unittest.main (failfast=True)

使用命令行的方式运行代码,结果如下:

test_add (__main__.TestMathFunc) ... ok

test_divide (__main__.TestMathFunc) ... FAIL

 

======================================================================

FAIL: test_divide (__main__.TestMathFunc)

----------------------------------------------------------------------

Traceback (most recent call last):

  File "unittest_demo.py", line 32, in test_divide

    self.assertEqual (2, divide (6, 2))

AssertionError: 2 != 3.0

 

----------------------------------------------------------------------

Ran 2 tests in 0.001s

 

FAILED (failures=1)

可以看到用例test_divide失败后,用例test_minus和test_multi没有继续运行

用例的参数化

unittest从Python3.4版本开始支持参数化,使用方法subTest()

具体代码如下

class ParaTest(unittest.TestCase):

   def test_1(self):

        for i in range(0, 4):

            with self.subTest(i=i):

                self.assertEqual(i % 2, 0)

输出结果如下:

上手unittest读这篇文章就够了_单元测试_08

详细的错误信息如下:

 

上手unittest读这篇文章就够了_python_09

 

标签:TestMathFunc,__,divide,unittest,就够,上手,test,self
From: https://blog.51cto.com/liwen629/7622450

相关文章

  • 从0到1上手Pytest
    引言如果你想快速上手pytest,只关注"Pytest必会知识点"章节就可以了(该章节已经能够解决基础的ui和接口自动化测试需求);如果你想要详细了解关于Fixture的使用方法,请关注“pytest高级用法”部分。 Pytest必会知识点基础介绍pytest是python的第三方单元测试框架,比unittest更简洁和......
  • 测试驱动技术(TDD)系列之1:一文带你上手测试数据驱动
    数据驱动的意义数据驱动,指在自动化测试中处理测试数据的方式。通常测试数据与功能函数分离,存储在功能函数的外部位置。在自动化测试运行时,数据驱动框架会读取数据源中的数据,把数据作为参数传递到功能函数中,并会根据数据的条数多次运行同一个功能函数。数据驱动的数据源可以是函数外......
  • 上手ElasticSearch必须了解的核心概念
    ElasticSearch概述ElasticSearch(简称ES)是一个分布式的使用REST接口的搜索引擎,属于非关系型数据库。它是在lucene的基础上进行研发的,隐藏了lucene的复杂性,提供简单易用的RESTfulApi接口。ES的分片相当于lucene的索引。ES属于Elastic公司,该公司同时拥有Logstash及......
  • Cobra眼睛蛇-强大的Golang CLI框架,快速上手的脚手架搭建项目工具,详细安装和使用
    Cobra眼睛蛇-强大的GolangCLI框架,快速上手的脚手架搭建项目工具,详细安装和使用。阅读过k8s源码的同学,应该都知道k8sScheduler、kubeadm、kubelet等核心组件的命令行交互全都是通过spf13写的Cobra库来实现。本文就来介绍下Cobra的相关概念及具体用法。关于Cobra是一个用于Go的CLI......
  • 免费ChatGPT使用,免费一键去除视频水印,有这免费的app就够用了
    ​大家好,我是小凉席,这款APP是我在大学期间试着做着玩的一款工具合集APP本来是想做着玩的,可是越做用户越多,直到现在群里那么多朋友支持,又给了我很大的动力来更新这款app从这款APP诞生到现在已经有两年时间了,一直秉持这  免费,无收费软件内工具有:视频去水印,图集去水印,视频转图片......
  • Vue3 基础 – 快速上手 & 常用指令
    1.在HTML网页中使用vue3的3个基本步骤a.通过 script 标签的 src 属性,在当前网页中全局引入vue3的脚本文件:<scriptsrc="https://unpkg.com/vue@3/dist/vue.global.js"></script>b.创建vue3的单页面应用程序实例://2.1从Vue对象中解构出createApp函数const{cre......
  • ChatGPT之集大成者,百宝箱,大智慧!有这一个就够了!以一敌百
    ChatGPT之集大成者,百宝箱,大智慧!有这一个就够了!以一敌百大模型、大智慧!有这一个就够了!以一敌百,值得拥有的百宝箱。引言 之前为大家推荐过一款:数倍生产力:ChatGPTPrompt这么玩......
  • rodert教你学FFmpeg实战这一篇就够了
    rodert教你学FFmpeg实战这一篇就够了前言todo有人问rodert哥这篇文章干货有多干,问就是,硌牙。ffmpeg有多强大,我想你都知道了,现在很多市场上的剪辑软件都是基于它做的,只是加了一些包装。读完本篇,你会发现一切如此简单。1.简介官网地址:https://trac.ffmpeg.org/wikiFFm......
  • 02安全原则:我们应该如何上手解决安全问题
    机密性、完整性、可用性简单说,以购买极客时间为例,机密性就是未付费用户无法学习这个专栏,完整性就是这个专栏不会变成别的其他方向的内容,可用性就是作为付费用户,能够随时学习这个专栏。那么该怎么解决安全问题呢黄金法则三部分:认证(Authentication)、授权(Authorization)、审计(Audi......
  • 全网最详细的OSPF原理总结,看这篇就够了!
    大家好,我的网工朋友。OSPF是一种基于链路状态的路由协议,也是专为IP开发的路由协议,直接运行在IP层上面。它从设计上保证了无路由环路。除此之外,IS-IS也是很常见的链路状态协议。为什么会出现OSPF?作为目前主流的IGP协议,OSPF主要是为了解决RIP的三大问题而出现的,比如:收敛很慢、容......