Python单元测试框架——unittest(一)


unittest介绍

unittest是Python标准库中自带的单元测试框架。unittest单元测试框架不仅可以适用于单元测试,还可以适用于接口测试、UI自动化测试等,它有一个很重要的特性:它是通过类(class)的方式将测试用例组织在一起。

什么是单元测试?

单元测试是开发者编写的一段代码,用于检验被测代码的一个很小的、很明确的功能是否正确。通常而言,一个单元测试是用于判断某个特定条件(或场景)下某个特定函数的行为。

什么是框架?

框架就是业内大佬定制研发的应用骨架,它对基础代码进行了封装并且提供一些API接口。其他开发者只需要直接调用封装好的API接口即可。可以节省普通开发者的很多代码编写,从而提升工作效率。

unittest核心工作原理

unittest中最核心的四个概念是:test case, test suite, test runner,test fixture

  • TestCase:测试用例,测试用例中会有很多测试方法,是单元测试中最小维度的测试行为。一个测试用例就是一个完整的测试单元,包括测试前准备的搭建(setUp)、执行测试代码(run),以及测试后环境的还原(tearDown)。
  • TestSuite:多个测试用例集合在一起,就是TestSuite,可以批量执行一个测试套件内所有的测试用例,而且TestSuite也可以嵌套TestSuite
  • TestRunner:测试的执行也是单元测试中非常重要的一个概念,一般单元测试框架中都会提供丰富的执行策略和执行结果。在unittest单元测试框架中,通过TextTestRunner类提供的run()方法来执行testsuite/testcasetestrunner可以使用图形界面,文本界面,或返回一个特殊的值等方式来表示测试执行的结果。
  • TestFixture:对一个测试用例环境的搭建和销毁,是一个fixture,通过覆盖TestCasesetUp()tearDown()方法来实现。用途是比如说在这个测试用例中需要访问数据库,那么可以在setUp()中建立数据库连接以及进行一些初始化,在tearDown()中清除在数据库中产生的数据,然后关闭连接。注意tearDown的过程很重要,要为以后的TestCase留下一个干净的环境。(Fixture很多资料翻译成夹具)

另,TestLoader也是比较重要的概念。TestLoader是用来加载TestCaseTestSuite中的,其中有几个loadTestsFrom__()方法,就是从各个地方寻找TestCase,创建它们的实例,然后addTestSuite中,再返回一个TestSuite实例

注:unittest的核心组件一共是5个,但很多资料上写的是4个,没有将TestLoader算在内。

可以通过dir命令查看unittest的属性和方法

>>> import unittest
>>> dir(unittest)
['BaseTestSuite', 'FunctionTestCase', 'IsolatedAsyncioTestCase', 'SkipTest', 'TestCase', 'TestLoader', 'TestProgram', 'TestResult', 'TestSuite', 'TextTestResult', 'TextTestRunner', '_TextTestResult', '__all__', '__builtins__', '__cached__', '__dir__', '__doc__', '__file__', '__getattr__', '__loader__', '__name__', '__package__', '__path__', '__spec__', '__unittest', 'addModuleCleanup', 'case', 'defaultTestLoader', 'expectedFailure', 'findTestCases', 'getTestCaseNames', 'installHandler', 'load_tests', 'loader', 'main', 'makeSuite', 'registerResult', 'removeHandler', 'removeResult', 'result', 'runner', 'signals', 'skip', 'skipIf', 'skipUnless', 'suite', 'util']

其中,下面几个是比较重要的:

unittest.TestCaseTestCase类,所有测试用例类都要继承的基类;

unittest.TestSuite()unittest框架的TestSuite()类是用来创建测试套件的;

unittest.main():将一个单元测试模块变成可直接运行的测试脚本,main()方法使用TestLoader类来搜索所有包含在该模块中“test”命名开头的测试方法,并自动执行它们,根据ASCII码的顺序加载测试用例;

unittest.TextTestRunnerunittest框架的TextTestRunner()类,通过该类下面的run()方法来运行suite所组装的测试用例,入参为suite测试套件;

unittest框架的使用步骤:

  1. 设计测试用例:通过继承unittest.TestCase设计具体的测试用例;
  2. 搜集测试用例:从多个py文件中收集并加载测试用例;
  3. 执行测试用例:将测试用例按照一定的顺序去执行并生成结果;
  4. 测试结果判断:通过断言去判断结果是正确;
  5. 输出测试结果:统计测试进度,通过率,生成报告;

TestCase测试用例

测试用例编写规范:

  • 测试模块需要导入uniitestimport unittest
  • 测试类必须继承unittest.TestCase
  • 测试方法必须以test开头
  • 测试用例执行的默认顺序是按照ASCII码的顺序执行的0~9 A~Z a~z
  • 在测试函数中使用断言来判断测试结果是否符合预期结果 (没有进行合理的断言不能称之为测试用例)

基于一个虚拟的PLS系统来进行测试用例的设计

  1. 设计登录的测试用例
import unittest


class PlsLogin(unittest.TestCase):
    
    # 测试用例01-测试老百登录
    def test_01_laobai(self):
        print("测试老百登录")
    
    # 测试用例02-测试nichengwe登录
    def test_02_nichengwe(self):
        print("测试nichengwe登录")
  1. 测试执行

测试执行可以通过以下几种方式进行:

a. 执行某个类中的某个测试方法: PycharmIDE的代码编辑器区,将光标放在要执行的某个测试方法中,右键运行“Run ‘Python tests for pls…’”

运行结果如下:

Testing started at 16:47 ...
Launching unittests with arguments python -m unittest pls_login.PlsLogin.test_01_laobai in /Users/laobai/workspaces/testdemo01/unittestdemo

Ran 1 test in 0.003s
测试老百登录

OK

Process finished with exit code 0

b. 执行某个类中的全部测试方法: PycharmIDE的代码编辑器区,将光标放在要执行类名附近,右键运行“Run ‘Python tests for pls…’”

运行结果如下:

Testing started at 18:07 ...
Launching unittests with arguments python -m unittest pls_login.PlsLogin in /Users/laobai/workspaces/testdemo01/unittestdemo



Ran 2 tests in 0.004s

OK
测试老百登录
测试nichengwe登录

Process finished with exit code 0

注:观察上面两种运行方式的输出结果,有两处输出内容需要注意

Launching unittests with arguments python -m unittest pls_login.PlsLogin.test_01_laobai in /Users/laobai/workspaces/testdemo01/unittestdemo
Launching unittests with arguments python -m unittest pls_login.PlsLogin in /Users/laobai/workspaces/testdemo01/unittestdemo

其中/Users/laobai/workspaces/testdemo01/unittestdemo是当前执行的测试用例所在的模块名(文件名)

python -m unittest pls_login.PlsLogin.test_01_laobaipython -m unittest pls_login.PlsLogin

是执行上面两个测试用例的命令。

先确保当前目录为/Users/laobai/workspaces/testdemo01/unittestdemo

$ python -m unittest pls_login.PlsLogin.test_01_laobai
测试老百登录
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK
$ python -m unittest pls_login.PlsLogin
测试老百登录
.测试nichengwe登录
.
----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK

注:python命令的参数-m

通过查看python的帮助文档可知为“以(二进制)脚本的形式去运行一个模块”

 $ python --help
usage: /Users/laobai/workspaces/testdemo01/venv/bin/python [option] ... [-c cmd | -m mod | file | -] [arg] ...
...
-m mod : run library module as a script (terminates option list)
...

c. 在上面两种方式的基础上,可以为unittest增加-v参数来获得更详细的输出结果信息

-v : --verbose 意思就是更详细的输出结果

$ python -m unittest -v pls_login.PlsLogin
test_01_laobai (pls_login.PlsLogin) ... 测试老百登录
ok
test_02_nichengwe (pls_login.PlsLogin) ... 测试nichengwe登录
ok

----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK

之前测试结果只显示两个点,增加了-v参数后,会显示具体测试的那个方法及所属类

  1. 在步骤1的基础上,在代码中增加main如下:

    import unittest
    
    
    class PlsLogin(unittest.TestCase):
    
        # 测试用例01-测试老百登录
        def test_01_laobai(self):
            print("测试老百登录")
    
        # 测试用例02-测试nichengwe登录
        def test_02_nichengwe(self):
            print("测试nichengwe登录")
    
    
    if __name__ == '__main__':
        unittest.main()

    增加的main对于上面两种通过在IDE中进行运行单元测试的方式没有影响。增加了main可以用如下命令在命令行中进行执行测试

    $ python pls_login.py 
    测试老百登录
    .测试nichengwe登录
    .
    ----------------------------------------------------------------------
    Ran 2 tests in 0.000s
    
    OK

    当然也可以增加-v参数

     $ python pls_login.py -v
    test_01_laobai (__main__.PlsLogin) ... 测试老百登录
    ok
    test_02_nichengwe (__main__.PlsLogin) ... 测试nichengwe登录
    ok
    
    ----------------------------------------------------------------------
    Ran 2 tests in 0.000s
    
    OK

    当然,也可以在IDE中,通过在main来执行用例

    注意:在命令行或main中可以添加verbosity参数来表示测试结果的信息复杂度,有0,1,2三个值

    ​ 0:静默模式,只能获得总的测试用例数和总的结果,比如一共10个用例,成功8个,失败1个,错误1个;

    ​ 1:默认模式,几乎与静默模式相同,成功的用例显示成.,失败的用例显示成F,错误的用例显示成E;

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

    main中书写格式为unittest.main(verbosity=2)

    而在命令行模式中,

    ​ 什么参数都不加,即为静默模式,等效于verbosity=1

    ​ 增加--quiet-q参数,即静默模式,等效于verbosity=0

    ​ 增加--verbose-v参数,即为详细模式,等效于verbosity=2

    1. 测试结果

      通过上面的执行的测试用例发现,会有如下的测试结果:

      .:成功

      import unittest
      
      
      class PlsLogin(unittest.TestCase):
      
          # 测试用例01-测试老百登录
          def test_01_laobai(self):
              print("测试老百登录")
      
          # 测试用例02-测试nichengwe登录
          def test_02_nichengwe(self):
              print("测试nichengwe登录")
      
      
      if __name__ == '__main__':
          unittest.main()
          
      
      $ python pls_login.py
      测试老百登录
      .测试nichengwe登录
      .
      ----------------------------------------------------------------------
      Ran 2 tests in 0.000s
      
      OK

      E:错误

      import unittest
      
      
      class PlsLogin(unittest.TestCase):
      
          # 测试用例01-测试老百登录
          def test_01_laobai(self):
              raise Exception("自定义异常")
              print("测试老百登录")
      
          # 测试用例02-测试nichengwe登录
          def test_02_nichengwe(self):
              print("测试nichengwe登录")
      
      
      if __name__ == '__main__':
          unittest.main()
      
          
          
      $ python pls_login.py
      E测试nichengwe登录
      .
      ======================================================================
      ERROR: test_01_laobai (__main__.PlsLogin)
      ----------------------------------------------------------------------
      Traceback (most recent call last):
        File "/Users/laobai/workspaces/testdemo01/unittestdemo/pls_login.py", line 11, in test_01_laobai
          raise Exception("自定义异常")
      Exception: 自定义异常
      
      ----------------------------------------------------------------------
      Ran 2 tests in 0.000s
      
      FAILED (errors=1)

      F:失败

      import unittest
      
      
      class PlsLogin(unittest.TestCase):
      
          # 测试用例01-测试老百登录
          def test_01_laobai(self):
              print("测试老百登录")
              self.assertTrue(0)
      
          # 测试用例02-测试nichengwe登录
          def test_02_nichengwe(self):
              print("测试nichengwe登录")
      
      
      if __name__ == '__main__':
          unittest.main()
          
          
      $ python pls_login.py
      测试老百登录
      F测试nichengwe登录
      .
      ======================================================================
      FAIL: test_01_laobai (__main__.PlsLogin)
      ----------------------------------------------------------------------
      Traceback (most recent call last):
        File "/Users/laobai/workspaces/testdemo01/unittestdemo/pls_login.py", line 12, in test_01_laobai
          self.assertTrue(0)
      AssertionError: 0 is not true
      
      ----------------------------------------------------------------------
      Ran 2 tests in 0.001s
      
      FAILED (failures=1)

      S:跳过

      import unittest
      
      
      class PlsLogin(unittest.TestCase):
      
          # 测试用例01-测试老百登录
          @unittest.skip("此用例不执行")
          def test_01_laobai(self):
              print("测试老百登录")
      
          # 测试用例02-测试nichengwe登录
          def test_02_nichengwe(self):
              print("测试nichengwe登录")
      
      
      if __name__ == '__main__':
          unittest.main()
          
      
          
      $ python pls_login.py
      s测试nichengwe登录
      .
      ----------------------------------------------------------------------
      Ran 2 tests in 0.000s
      
      OK (skipped=1)
    2. 测试用例的执行顺序

      a. 默认顺序是按照ASCII码的顺序执行的0~9 A~Z a~z

      b. 在测试用例用例方法命名时,可以增加序号来自定义顺序,比如上面的例子:test_01_laobaitest_02_nichengwe

TestSuite测试套件

上面的例子简单说明了编写一个简单的单一的测试,按照默认的顺序执行测试用例,若是要自定义测试用例的执行顺序,就需要用到了TestSuite,添加到TestSuite中的测试用例会按照添加的顺序进行执行的。测试用例存在多个文件中,也可以用TestSuite来组织执行。

使用TestSuite测试套件的步骤:

  1. 定义测试套件(suite = unittest.TestSuite()
  2. 向测试套件中添加测试用例(按照添加顺序执行)
  3. 运行套件中的用例

先写两个测试用例:

pls_register.py

import unittest


class PlsRegister(unittest.TestCase):

    # 测试用例01-测试注册账号-老百
    def test_01_laobai(self):
        print("测试注册账号-老百")

    # 测试用例02-测试注册账号-nichengwe
    def test_02_nichengwe(self):
        print("测试注册账号-nichengwe")

    # 测试用例03-测试注册账号-baby2016
    def test_03_baby2016(self):
        print("测试注册账号-baby2016")


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

pls_login.py

import unittest


class PlsLogin(unittest.TestCase):

    # 测试用例01-测试老百登录
    def test_01_laobai(self):
        print("测试老百登录")

    # 测试用例02-测试nichengwe登录
    def test_02_nichengwe(self):
        print("测试nichengwe登录")

    # 测试用例02-测试baby2016登录
    def test_03_baby2016(self):
        print("测试baby2016登录")


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

addTest

通过使用addTest向测试套件中添加用例,每次添加一个用例,测试执行时按照添加用例的顺序执行。

import unittest


class PlsRegister(unittest.TestCase):

    # 测试用例01-测试注册账号-老百
    def test_01_laobai(self):
        print("测试注册账号-老百")

    # 测试用例02-测试注册账号-nichengwe
    def test_02_nichengwe(self):
        print("测试注册账号-nichengwe")

    # 测试用例03-测试注册账号-baby2016
    def test_03_baby2016(self):
        print("测试注册账号-baby2016")


if __name__ == '__main__':
    suite = unittest.TestSuite()
    suite.addTest(PlsRegister('test_02_nichengwe'))
    suite.addTest(PlsRegister('test_03_baby2016'))
    suite.addTest(PlsRegister('test_01_laobai'))
    unittest.main(defaultTest='suite', verbosity=2)
    # unittest.TextTestRunner(verbosity=2).run(suite)

执行结果如下:

测试注册账号-nichengwe
测试注册账号-baby2016
测试注册账号-老百
test_02_nichengwe (__main__.PlsRegister) ... ok
test_03_baby2016 (__main__.PlsRegister) ... ok
test_01_laobai (__main__.PlsRegister) ... ok

----------------------------------------------------------------------
Ran 3 tests in 0.001s

OK

Process finished with exit code 0

可以看到执行的结果是按照添加到套件里的顺序进行的。

unittest.main(defaultTest='suite', verbosity=2)unittest.TextTestRunner(verbosity=2).run(suite)两种写法都可以。

注意:

在PycharmIDE中执行测试用例时,可能会出现添加了部分用例或者某种顺序,但执行的时候是全部用例及默认顺序执行的。(在命令行中执行是正确的,按照添加到套件的顺序执行)

这里需要编辑修改Edit Configurations,新建一个pythonconfiguration,在Script path中选择对应的文件

然后在IDE中选择新建的configuration,通过右侧的执行按钮进行用例的执行即可。

addTests

可以通过addTests批量添加多个用例

import unittest


class PlsRegister(unittest.TestCase):

    # 测试用例01-测试注册账号-老百
    def test_01_laobai(self):
        print("测试注册账号-老百")

    # 测试用例02-测试注册账号-nichengwe
    def test_02_nichengwe(self):
        print("测试注册账号-nichengwe")

    # 测试用例03-测试注册账号-baby2016
    def test_03_baby2016(self):
        print("测试注册账号-baby2016")


if __name__ == '__main__':
    suite = unittest.TestSuite()
    testcases = [PlsRegister('test_02_nichengwe'), PlsRegister('test_01_laobai'), PlsRegister('test_03_baby2016')]
    suite.addTests(testcases)
    unittest.main(defaultTest='suite', verbosity=2)

执行结果:

test_02_nichengwe (__main__.PlsRegister) ... ok
test_01_laobai (__main__.PlsRegister) ... ok
test_03_baby2016 (__main__.PlsRegister) ... ok

----------------------------------------------------------------------
Ran 3 tests in 0.000s

OK
测试注册账号-nichengwe
测试注册账号-老百
测试注册账号-baby2016

Process finished with exit code 0

也可以跨文件进行添加:

import pls_register
import unittest


class PlsLogin(unittest.TestCase):

    # 测试用例01-测试老百登录
    def test_01_laobai(self):
        print("测试老百登录")

    # 测试用例02-测试nichengwe登录
    def test_02_nichengwe(self):
        print("测试nichengwe登录")

    # 测试用例02-测试baby2016登录
    def test_03_baby2016(self):
        print("测试baby2016登录")


if __name__ == '__main__':
    suite = unittest.TestSuite()
    testcases = [pls_register.PlsRegister('test_02_nichengwe'), pls_register.PlsRegister('test_01_laobai'), pls_register.PlsRegister('test_03_baby2016'),PlsLogin('test_03_baby2016'),PlsLogin('test_01_laobai'),PlsLogin('test_02_nichengwe')]
    suite.addTests(testcases)
    unittest.main(defaultTest='suite', verbosity=2)

运行结果:

$ python pls_login.py
test_02_nichengwe (pls_register.PlsRegister) ... 测试注册账号-nichengwe
ok
test_01_laobai (pls_register.PlsRegister) ... 测试注册账号-老百
ok
test_03_baby2016 (pls_register.PlsRegister) ... 测试注册账号-baby2016
ok
test_03_baby2016 (__main__.PlsLogin) ... 测试baby2016登录
ok
test_01_laobai (__main__.PlsLogin) ... 测试老百登录
ok
test_02_nichengwe (__main__.PlsLogin) ... 测试nichengwe登录
ok

----------------------------------------------------------------------
Ran 6 tests in 0.001s

ok

discover

上面的两个例子是添加多个用例,但用例过多时会比较繁琐。可以使用discover自动搜寻测试用例

discover()有两个参数start_dir在哪个目录搜索和pattern在哪个文件下搜索,pattern的默认取值是'test*.py'

import os
import unittest


class PlsLogin(unittest.TestCase):

    # 测试用例01-测试老百登录
    def test_01_laobai(self):
        print("测试老百登录")

    # 测试用例02-测试nichengwe登录
    def test_02_nichengwe(self):
        print("测试nichengwe登录")

    # 测试用例02-测试baby2016登录
    def test_03_baby2016(self):
        print("测试baby2016登录")


if __name__ == '__main__':
    suite = unittest.TestSuite()
    testcases = unittest.defaultTestLoader.discover(start_dir=os.getcwd(), pattern='*.py')
    suite.addTests(testcases)
    unittest.main(defaultTest='suite', verbosity=2)

执行结果:

$ python pls_login.py
test_01_laobai (pls_login.PlsLogin) ... 测试老百登录
ok
test_02_nichengwe (pls_login.PlsLogin) ... 测试nichengwe登录
ok
test_03_baby2016 (pls_login.PlsLogin) ... 测试baby2016登录
ok
test_01_laobai (pls_register.PlsRegister) ... 测试注册账号-老百
ok
test_02_nichengwe (pls_register.PlsRegister) ... 测试注册账号-nichengwe
ok
test_03_baby2016 (pls_register.PlsRegister) ... 测试注册账号-baby2016
ok

----------------------------------------------------------------------
Ran 6 tests in 0.001s

OK

其中,os.getcwd()是获取当前目录

TestLoader

可以通过TestLoader测试装载器去装载测试用例所在的类

import unittest
import pls_register
import pls_login

if __name__ == '__main__':
    loader = unittest.TestLoader()
    suite1 = loader.loadTestsFromTestCase(pls_register.PlsRegister)
    suite2 = loader.loadTestsFromTestCase(pls_login.PlsLogin)

    suite = unittest.TestSuite([suite1, suite2])

    unittest.TextTestRunner(verbosity=2).run(suite)

运行结果:

测试注册账号-老百
测试注册账号-nichengwe
测试注册账号-baby2016
测试老百登录
测试nichengwe登录
测试baby2016登录
test_01_laobai (pls_register.PlsRegister) ... ok
test_02_nichengwe (pls_register.PlsRegister) ... ok
test_03_baby2016 (pls_register.PlsRegister) ... ok
test_01_laobai (pls_login.PlsLogin) ... ok
test_02_nichengwe (pls_login.PlsLogin) ... ok
test_03_baby2016 (pls_login.PlsLogin) ... ok

----------------------------------------------------------------------
Ran 6 tests in 0.002s

OK

Process finished with exit code 0

TestFixture测试夹具

对一个测试用例环境的搭建和销毁,是一个fixture,通过覆盖TestCasesetUp()tearDown()方法来实现。根据应用范围,有如下4个:

setUp:每运行一个用例前都会执行一次;

tearDown:每运行一个用例或都会执行一次;

setUpClass:在测试用例类执行开始前执行,只执行一次;

tearDownClass:在测试用例类执行结束后执行,只执行一次;

setUpModule:在模块测试用例模块执行开始前执行,只执行一次;

tearDownModule:在模块测试用例模块执行结束后执行,只执行一次;

import unittest

def setUpModule():
    print("模块级的夹具开始前")

def tearDownModule():
    print("模块级的夹具结束后")

class PlsLogin(unittest.TestCase):

    @classmethod
    def setUpClass(cls) -> None:
        print("setUpClass:在当前类中用例执行之前只执行一次")

    def setUp(self) -> None:
        print("\nsetUp:在测试用例之前的准备工作")

    # 测试用例01-测试老百登录
    def test_01_laobai(self):
        print("测试老百登录")

    # 测试用例02-测试nichengwe登录
    def test_02_nichengwe(self):
        print("测试nichengwe登录")

    # 测试用例02-测试baby2016登录
    def test_03_baby2016(self):
        print("测试baby2016登录")

    def tearDown(self) -> None:
        print("tearDown:在每个用例执行完成之后执行一次")

    @classmethod
    def tearDownClass(cls) -> None:
        print("tearDownClass:在每个类执行完之后执行一次")

if __name__ == '__main__':
    suite = unittest.TestSuite()
    testcases = [PlsLogin('test_03_baby2016'),PlsLogin('test_01_laobai'),PlsLogin('test_02_nichengwe')]
    suite.addTests(testcases)
    unittest.main(defaultTest='suite', verbosity=2)

执行结果:

$ python pls_login.py
模块级的夹具开始前
setUpClass:在当前类中用例执行之前只执行一次
test_03_baby2016 (__main__.PlsLogin) ... 
setUp:在测试用例之前的准备工作
测试baby2016登录
tearDown:在每个用例执行完成之后执行一次
ok
test_01_laobai (__main__.PlsLogin) ... 
setUp:在测试用例之前的准备工作
测试老百登录
tearDown:在每个用例执行完成之后执行一次
ok
test_02_nichengwe (__main__.PlsLogin) ... 
setUp:在测试用例之前的准备工作
测试nichengwe登录
tearDown:在每个用例执行完成之后执行一次
ok
tearDownClass:在每个类执行完之后执行一次
模块级的夹具结束后

----------------------------------------------------------------------
Ran 3 tests in 0.000s

OK

注:针对模块的夹具很少使用,之后的例子中将不在设计Module夹具。

针对一个项目,测试用例会很多(写在多个py文件中,每个py文件中有多个用例),可以将夹具提取出来,不用每个py文件中都写。

新建pls_unit.py

import unittest


class PlsUnit(unittest.TestCase):

    @classmethod
    def setUpClass(cls) -> None:
        print("setUpClass:在当前类中用例执行之前只执行一次")

    def setUp(self) -> None:
        print("\nsetUp:在测试用例之前的准备工作")

    def tearDown(self) -> None:
        print("tearDown:在每个用例执行完成之后执行一次")

    @classmethod
    def tearDownClass(cls) -> None:
        print("tearDownClass:在每个类执行完之后执行一次")

这样测试用例的py中就不用写夹具了,导入pls_unit即可

修改pls_register.py如下:

import unittest
import pls_unit


class PlsRegister(pls_unit.PlsUnit):

    # 测试用例01-测试注册账号-老百
    def test_01_laobai(self):
        print("测试注册账号-老百")

    # 测试用例02-测试注册账号-nichengwe
    def test_02_nichengwe(self):
        print("测试注册账号-nichengwe")

    # 测试用例03-测试注册账号-baby2016
    def test_03_baby2016(self):
        print("测试注册账号-baby2016")

修改pls_login.py如下:

import unittest
import pls_unit


class PlsLogin(pls_unit.PlsUnit):

    # 测试用例01-测试老百登录
    def test_01_laobai(self):
        print("测试老百登录")

    # 测试用例02-测试nichengwe登录
    def test_02_nichengwe(self):
        print("测试nichengwe登录")

    # 测试用例02-测试baby2016登录
    def test_03_baby2016(self):
        print("测试baby2016登录")

新建main.py

import os
import unittest

if __name__ == '__main__':
    suite = unittest.TestSuite()
    testcases = unittest.defaultTestLoader.discover(os.getcwd(), "*.py")
    suite.addTests(testcases)
    unittest.TextTestRunner(verbosity=2).run(suite)

运行main.py结果如下:

test_01_laobai (pls_register.PlsRegister) ... ok
test_02_nichengwe (pls_register.PlsRegister) ... ok
test_03_baby2016 (pls_register.PlsRegister) ... ok
test_01_laobai (pls_login.PlsLogin) ... ok
test_02_nichengwe (pls_login.PlsLogin) ... ok
test_03_baby2016 (pls_login.PlsLogin) ... ok

----------------------------------------------------------------------
Ran 6 tests in 0.001s

OK
测试注册账号-老百
测试注册账号-nichengwe
测试注册账号-baby2016
setUpClass:在当前类中用例执行之前只执行一次

setUp:在测试用例之前的准备工作
测试老百登录
tearDown:在每个用例执行完成之后执行一次

setUp:在测试用例之前的准备工作
测试nichengwe登录
tearDown:在每个用例执行完成之后执行一次

setUp:在测试用例之前的准备工作
测试baby2016登录
tearDown:在每个用例执行完成之后执行一次
tearDownClass:在每个类执行完之后执行一次

Process finished with exit code 0

TestRunner测试运行器

运行器,前面已经讲了,一般通过runner来调用suite去执行,unittest框架的TextTestRunner()类,通过该类下面的run()方法运行suite所组装的测试用例,入参为suite测试套件

import os
import unittest

if __name__ == '__main__':
    suite = unittest.TestSuite()
    testcases = unittest.defaultTestLoader.discover(os.getcwd(), "*.py")
    suite.addTests(testcases)
    unittest.TextTestRunner(verbosity=2).run(suite)
mport unittest
import pls_register
import pls_login

if __name__ == '__main__':
    loader = unittest.TestLoader()
    suite1 = loader.loadTestsFromTestCase(pls_register.PlsRegister)
    suite2 = loader.loadTestsFromTestCase(pls_login.PlsLogin)

    suite = unittest.TestSuite([suite1, suite2])
    unittest.TextTestRunner(verbosity=2).run(suite)

常用方法

跳过测试用例

skip装饰器

  • @unittest.skip(reason):无条件跳过装饰的测试用例,reason参数是跳过用例的原因;

    import unittest
    
    class PlsRegister(unittest.TestCase):
    
        # 测试用例01-测试注册账号-老百
        @unittest.skip("老百是系统初始化账号无需注册")
        def test_01_laobai(self):
            print("测试注册账号-老百")
    
        # 测试用例02-测试注册账号-nichengwe
        def test_02_nichengwe(self):
            print("测试注册账号-nichengwe")
    
        # 测试用例03-测试注册账号-baby2016
        def test_03_baby2016(self):
            print("测试注册账号-baby2016")
    
    
    if __name__ == '__main__':
        suite = unittest.TestSuite()
        testcases = [PlsRegister('test_02_nichengwe'), PlsRegister('test_01_laobai'), PlsRegister('test_03_baby2016')]
        suite.addTests(testcases)
        unittest.main(defaultTest='suite', verbosity=2)

    执行结果:

    $ python pls_register.py 
    test_02_nichengwe (__main__.PlsRegister) ... 测试注册账号-nichengwe
    ok
    test_01_laobai (__main__.PlsRegister) ... skipped '老百是系统初始化账号无需注册'
    test_03_baby2016 (__main__.PlsRegister) ... 测试注册账号-baby2016
    ok
    
    ----------------------------------------------------------------------
    Ran 3 tests in 0.000s
    
    OK (skipped=1)
  • @unittest.skipIf(condition, reason)condition条件为真时,跳过装饰的测试用例,reason参数是跳过用例的原因;

    import unittest
    
    class PlsRegister(unittest.TestCase):
        age = 17
    
        # 测试用例01-测试注册账号-老百
        @unittest.skip("老百是系统初始化账号无需注册")
        def test_01_laobai(self):
            print("测试注册账号-老百")
    
        # 测试用例02-测试注册账号-nichengwe
        @unittest.skipIf(age <= 18, "未成年人不能注册")
        def test_02_nichengwe(self):
            print("测试注册账号-nichengwe")
    
        # 测试用例03-测试注册账号-baby2016
        def test_03_baby2016(self):
            print("测试注册账号-baby2016")
    
    
    if __name__ == '__main__':
        suite = unittest.TestSuite()
        testcases = [PlsRegister('test_02_nichengwe'), PlsRegister('test_01_laobai'), PlsRegister('test_03_baby2016')]
        suite.addTests(testcases)
        unittest.main(defaultTest='suite', verbosity=2)

    执行结果:

    $ python pls_register.py 
    test_02_nichengwe (__main__.PlsRegister) ... skipped '未成年人不能注册'
    test_01_laobai (__main__.PlsRegister) ... skipped '老百是系统初始化账号无需注册'
    test_03_baby2016 (__main__.PlsRegister) ... 测试注册账号-baby2016
    ok
    
    ----------------------------------------------------------------------
    Ran 3 tests in 0.001s
    
    OK (skipped=2)
  • @unittest.skipUnless(condition, reason)condition条件为假时,跳过装饰的测试用例,reason参数是跳过用例的原因;

    import unittest
    
    class PlsRegister(unittest.TestCase):
        age = 17
    
        # 测试用例01-测试注册账号-老百
        @unittest.skip("老百是系统初始化账号无需注册")
        def test_01_laobai(self):
            print("测试注册账号-老百")
    
        # 测试用例02-测试注册账号-nichengwe
        @unittest.skipIf(age <= 18, "未成年人不能注册")
        def test_02_nichengwe(self):
            print("测试注册账号-nichengwe")
    
        # 测试用例03-测试注册账号-baby2016
        @unittest.skipUnless(age <= 16 or age >= 18, "只有17岁的无法注册")
        def test_03_baby2016(self):
            print("测试注册账号-baby2016")
    
    
    if __name__ == '__main__':
        suite = unittest.TestSuite()
        testcases = [PlsRegister('test_02_nichengwe'), PlsRegister('test_01_laobai'), PlsRegister('test_03_baby2016')]
        suite.addTests(testcases)
        unittest.main(defaultTest='suite', verbosity=2)

    执行结果:

    $ python pls_register.py 
    test_02_nichengwe (__main__.PlsRegister) ... skipped '未成年人不能注册'
    test_01_laobai (__main__.PlsRegister) ... skipped '老百是系统初始化账号无需注册'
    test_03_baby2016 (__main__.PlsRegister) ... skipped '只有17岁的无法注册'
    
    ----------------------------------------------------------------------
    Ran 3 tests in 0.000s
    
    OK (skipped=3)

TestCase.skipTest()方法

可以在需要跳过的用例中增加self.skipTest()来跳过当前用例,括号中填入跳过原因

import unittest

class PlsRegister(unittest.TestCase):
    age = 17

    # 测试用例01-测试注册账号-老百
    def test_01_laobai(self):
        self.skipTest("跳过此用例。。。")
        print("测试注册账号-老百")

    # 测试用例02-测试注册账号-nichengwe
    def test_02_nichengwe(self):
        print("测试注册账号-nichengwe")

    # 测试用例03-测试注册账号-baby2016
    def test_03_baby2016(self):
        print("测试注册账号-baby2016")


if __name__ == '__main__':
    suite = unittest.TestSuite()
    testcases = [PlsRegister('test_02_nichengwe'), PlsRegister('test_01_laobai'), PlsRegister('test_03_baby2016')]
    suite.addTests(testcases)
    unittest.main(defaultTest='suite', verbosity=2)

执行结果:

$ python pls_register.py 
test_02_nichengwe (__main__.PlsRegister) ... 测试注册账号-nichengwe
ok
test_01_laobai (__main__.PlsRegister) ... skipped '跳过此用例。。。'
test_03_baby2016 (__main__.PlsRegister) ... 测试注册账号-baby2016
ok

----------------------------------------------------------------------
Ran 3 tests in 0.000s

OK (skipped=1)

命令行执行测试

指定用例

  • 指定测试模块

    $ python -m unittest pls_register pls_login
    s测试注册账号-nichengwe
    .测试注册账号-baby2016
    .测试老百登录
    .测试nichengwe登录
    .测试baby2016登录
    .
    ----------------------------------------------------------------------
    Ran 6 tests in 0.000s
    
    OK (skipped=1)
  • 指定测试类

    $ python -m unittest pls_register.PlsRegister pls_login.PlsLogin
    s测试注册账号-nichengwe
    .测试注册账号-baby2016
    .测试老百登录
    .测试nichengwe登录
    .测试baby2016登录
    .
    ----------------------------------------------------------------------
    Ran 6 tests in 0.000s
    
    OK (skipped=1)
  • 指定测试方法(用例)

    $ python -m unittest pls_register.PlsRegister.test_02_nichengwe
    测试注册账号-nichengwe
    .
    ----------------------------------------------------------------------
    Ran 1 test in 0.000s
    
    OK

自动发现用例

前提条件:由于unittest自动发现测试用例的文件模板是test*.py,所以,将pls_register.py修改为test_pls_register.py,将pls_login.py改为test_pls_login.py

  • 自动搜索当前目录

    $ python -m unittest
    测试老百登录
    .测试nichengwe登录
    .测试baby2016登录
    .s测试注册账号-nichengwe
    .测试注册账号-baby2016
    .
    ----------------------------------------------------------------------
    Ran 6 tests in 0.000s
    
    OK (skipped=1)
    $ python -m unittest discover
    测试老百登录
    .测试nichengwe登录
    .测试baby2016登录
    .s测试注册账号-nichengwe
    .测试注册账号-baby2016
    .
    ----------------------------------------------------------------------
    Ran 6 tests in 0.000s
    
    OK (skipped=1)
  • 自动搜索指定目录及文件的名称模式

    $ python -m unittest discover -s /Users/laobai/workspaces/testdemo01/unittestdemo -p "test_*.py"
    测试老百登录
    .测试nichengwe登录
    .测试baby2016登录
    .s测试注册账号-nichengwe
    .测试注册账号-baby2016
    .
    ----------------------------------------------------------------------
    Ran 6 tests in 0.000s
    
    OK (skipped=1)

文章作者: 老百
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 老百 !
 上一篇
Python单元测试框架——unittest(二) Python单元测试框架——unittest(二)
unittest单元测试框架是受到JUnit的启发,与其他语言中的主流单元测试框架有着相似的风格。其支持测试自动化,配置共享和关机代码测试。支持将测试样例聚合到测试集中,并将测试与报告框架独立。
2022-11-26
下一篇 
Python之魔术方法 Python之魔术方法
在Python中,所有以“__”双下划线包起来的方法,都统称为“Magic Method”,中文称『魔术方法』,例如类的初始化方法__init__
2022-11-15
  目录