Python 单元测试框架 unittest

虽然并不是专门做测试的,但是测试方面的知识涉猎一下还是没什么坏处的。

具体细节的可以访问官网文档查看:unittest --- 单元测试框架 — Python 3.10.1 文档

常用的断言方法: 

Method

Checks that

assertEqual(a, b)

a == b

assertNotEqual(a, b)

a != b

assertTrue(x)

bool(x) is True

assertFalse(x)

bool(x) is False

assertIs(a, b)

a is b

assertIsNot(a, b)

a is not b

assertIsNone(x)

x is None

assertIsNotNone(x)

x is not None

assertIn(a, b)

a in b

assertNotIn(a, b)

a not in b

assertIsInstance(a, b)

isinstance(a, b)

assertNotIsInstance(a, b)

not isinstance(a, b)

基本实例

继承 unittest.TestCase 就创建了一个测试样例。每个独立的测试是类的方法,这些方法的命名都以 test 开头。 这个命名约定告诉测试运行者类的哪些方法表示测试。如果类里边的函数不是以 test 开头,那么单元测试的时候是不会主动去执行这个函数的。

通过 setUp() 和 tearDown() 方法,可以设置测试开始前与完成后需要执行的指令。注意测试框架会自动地为每个单独测试调用前置方法

import unittest


class TestStringMethods(unittest.TestCase):

    def test_upper(self):
        self.assertEqual('foo'.upper(), 'FOO')

    def test_isupper(self):
        self.assertTrue('FOO'.isupper())
        self.assertFalse('Foo'.isupper())

    def test_split(self):
        s = 'hello world'
        self.assertEqual(s.split(), ['hello', 'world'])
        # check that s.split fails when the separator is not a string
        with self.assertRaises(TypeError):
            s.split(2)


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

在调用测试脚本时添加 -v 参数使 unittest.main() 显示更为详细的信息(比如说测试的哪个类的哪个方法):

[root@master ~]# python3 -m unittest test
...
----------------------------------------------------------------------
Ran 3 tests in 0.001s

OK

[root@master ~]# python3 -m unittest -v test
test_isupper (test.TestStringMethods) ... ok
test_split (test.TestStringMethods) ... ok
test_upper (test.TestStringMethods) ... ok

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

OK

命令行接口

unittest 模块可以通过命令行运行模块、类和独立测试方法的测试:

[root@master ~]# python3 -m unittest test
...
----------------------------------------------------------------------
Ran 3 tests in 0.001s

OK
[root@master ~]# python3 -m unittest test.TestStringMethods.test_isupper
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK
[root@master ~]# python3 -m unittest -v test.TestStringMethods.test_isupper
test_isupper (test.TestStringMethods) ... ok

----------------------------------------------------------------------
Ran 1 test in 0.000s

OK
[root@master ~]# python3 -m unittest -h
usage: python3 -m unittest [-h] [-v] [-q] [--locals] [-f] [-c] [-b]
                           [-k TESTNAMEPATTERNS]
                           [tests [tests ...]]

positional arguments:
  tests                a list of any number of test modules, classes and test
                       methods.

optional arguments:
  -h, --help           show this help message and exit
  -v, --verbose        Verbose output
  -q, --quiet          Quiet output
  --locals             Show local variables in tracebacks
  -f, --failfast       Stop on first fail or error
  -c, --catch          Catch Ctrl-C and display results so far
  -b, --buffer         Buffer stdout and stderr during tests
  -k TESTNAMEPATTERNS  Only run tests which match the given substring

Examples:
  python3 -m unittest test_module               - run tests from test_module
  python3 -m unittest module.TestClass          - run tests from module.TestClass
  python3 -m unittest module.Class.test_method  - run specified test method
  python3 -m unittest path/to/test_file.py      - run tests from test_file.py

...

For test discovery all test modules must be importable from the top level
directory of the project.

探索性测试

其实简单理解就是,一个目录下可能有多个单元测试的脚步,自己一个个去跑脚本显然比较麻烦,用探索性测试就可以让它自己去定位哪些脚本里边有测试样例,并且主动去进行测试。

[root@master ~]# python3 -m unittest discover /root
...
----------------------------------------------------------------------
Ran 3 tests in 0.000s

OK
[root@master ~]# python3 -m unittest discover /root/test

----------------------------------------------------------------------
Ran 0 tests in 0.000s

OK
[root@master ~]# mv ttt.txt test2.py
[root@master ~]# python3 -m unittest discover /root/
....F
======================================================================
FAIL: test_lower (test2.TestStringMethods)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/root/test2.py", line 7, in test_lower
    self.assertEqual('foo'.lower(), 'FOO')
AssertionError: 'foo' != 'FOO'
- foo
+ FOO


----------------------------------------------------------------------
Ran 5 tests in 0.001s

FAILED (failures=1)

跳过测试和预计失败

跳过测试

有时候我们可能不想去测试某个测试方法,这时候就可以 @unittest.skip() 去装饰那个要跳过的测试方法了。

import unittest


class TestStringMethods(unittest.TestCase):

    @unittest.skip("demonstrating skipping")
    def test_nothing(self):
        self.fail("shouldn't happen")

    def test_upper(self):
        self.assertEqual('foo'.upper(), 'FOO')

    def test_isupper(self):
        self.assertTrue('FOO'.isupper())
        self.assertFalse('Foo'.isupper())


if __name__ == '__main__':
    unittest.main()
.s.
----------------------------------------------------------------------
Ran 3 tests in 0.001s

OK (skipped=1)

预计失败

@unittest.expectedFailure 这块和 @unittest.skip() 不同的是,skip 会跳过测试,但 expectedFailure 还是继续会进行测试的。只不过如果测试失败的话,不会影响最后的测试结果(毕竟和预期失败的结果相符);如果 expectedFailure 预期失败,测试却反而成功的话,会得到类似如下提示:

FAILED (unexpected successes=1)

如果提前知道某个测试方法通不过测试,但是又不想让它影响最后的测试结果,就可以使用装饰器把它标注成 “预期失败”:

import unittest


class TestStringMethods(unittest.TestCase):

    @unittest.expectedFailure
    def test_nothing(self):
        self.fail("shouldn't happen")

    def test_upper(self):
        self.assertEqual('foo'.upper(), 'FOO')

    def test_isupper(self):
        self.assertTrue('FOO'.isupper())
        self.assertFalse('Foo'.isupper())


if __name__ == '__main__':
    unittest.main()
.x.
----------------------------------------------------------------------
Ran 3 tests in 0.000s

OK (expected failures=1)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值