python如何进行单元测试_python 单元测试 -- unittest

本文介绍了Python单元测试框架unittest的使用,包括编写测试用例、测试初始化和清理、测试套件以及如何使用mock进行模拟测试。通过示例解释了如何对函数和类进行单元测试,并展示了测试用例的执行和结果分析。
摘要由CSDN通过智能技术生成

@(python)

单元测试是对程序中的单个子程序、函数、过程进行的测试,面向白盒测试。

单元测试测试覆盖常用子程序的输入组合,边界条件和异常处理,尽可能保证单元测试代码简洁,避免单测本身代码有 bug 影响对测试对象的测试结果。

python 提供单元测试框架 unittest,

简单编写一个模块 calculator.py ,作为单元测试对象

#!/usr/bin/env python

# coding=utf-8

def my_print(str):

pass

#print(str)

class Calculator():

__version__ = 4

def __init__(self, a, b):

my_print("cal init")

self.a = int(a)

self.b = int(b)

def __del__(self):

my_print("cal del")

def add(self):

return self.a + self.b

def sub(self):

return self.a - self.b

def mul(self):

return self.a * self.b

def div(self):

return self.a / self.b

编写测试用例 (test case)

如上, 我们为该模块编写对应的单元测试,取名 testCalculator.py :

#!/usr/bin/env python

# coding=utf-8

import unittest

from calculator import Calculator

class CalculatorTest(unittest.TestCase):

def test_add_0(self):

cal = Calculator(8, 4)

result = cal.add()

self.assertEqual(result, 12)

def test_add_1(self):

cal = Calculator(8, 4)

result = cal.add()

self.assertNotEqual(result, 12)

def will_not_callme(self):

print("lalalla")

if __name__ == "__main__":

unittest.main()

简单地编写了两个对模块方法 add() 的测试用例。编写单元测试,我们需要对应测试的对象实现一个类,继承 unittest.TestCase。

测试类 CalculatorTest 中的测试用例都是以 test_, 其他方法在执行脚本的时候框架不会直接调用执行。

对应目标模块的各个方法编写测试用例,使用断言判断结果,注意使用的断言是 unittest.TestCase内置的,这样才能保证不会由于某个用例断言失败而直接退出执行。

执行 运行结果如下,可以看到,没有通过的例子断言了错误的行号,可以快速定位问题。

$ python testCalculator.py -v

test_add_0 (__main__.CalculatorTest) ... ok

test_add_1 (__main__.CalculatorTest) ... FAIL

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

FAIL: test_add_1 (__main__.CalculatorTest)

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

Traceback (most recent call last):

File "aa.py", line 16, in test_add_1

self.assertNotEqual(result, 12)

AssertionError: 12 == 12

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

Ran 2 tests in 0.002s

FAILED (failures=1)

内置的断言

Method

Checks

that

New in

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

3.1

assertIsNot(a, b)

a is not b

3.1

assertIsNone(x)

x is None

3.1

assertIsNotNone(x)

x is not None

3.1

assertIn(a, b)

a in b

3.1

assertNotIn(a, b)

a not in b

3.1

assertIsInstance(a, b)

isinstance(a, b)

3.2

assertNotIsInstance(a, b)

not isinstance(a, b)

3.2

测试初始化和清理(test fixture)

看到上面的例子,每次写一个测试用例都要重新定义一个测试实例 cal, 显得很重复,但是直接在测试类初始化函数定义的话又怕用例之间相互干扰。还有就是,有些测试,需要测试前构建测试场景,测试结束后清理。

类似以上的问题,unittest 提供了几个方法实现。

setUp() : 执行每个测试用例前都会调用,执行准备

tearDown() : 执行完每个用例后都会调用,执行清理

对应上面两个方法,下面两个在测试类函数开始和结束调用

setUpClass()

tearDownClass()

测试套件 (test suit)

测试套件是多个测试用例的集合

结合上面内容,看个相对完整的测试套件 :

#!/usr/bin/env python

# coding=utf-8

import sys

import unittest

from calculator import Calculator

class CalculatorTest(unittest.TestCase):

def setUp(self):

self.cal = Calculator(8, 4)

#assert 1 == 2, "test if setUp error"

def tearDown(self):

self.cal = None

def test_add(self):

'''

des : test add

'''

result = self.cal.add()

self.assertEqual(result, 12)

# if use python builtin assert , it will stop run

# assert result == 12, "add error"

def test_sub(self):

result = self.cal.sub()

self.assertEqual(result, 4)

def test_mul(self):

result = self.cal.mul()

self.assertEqual(result, 32)

def test_div(self):

result = self.cal.div()

self.assertEqual(result, 2)

@unittest.skip('just skip')

def test_div_1(self):

result = self.cal.div()

self.assertEqual(result, 3)

@unittest.skipIf(Calculator.__version__ < 5, 'not support this library')

def test_div_2(self):

result = self.cal.div()

self.assertEqual(result, 2)

@unittest.skipIf(Calculator.__version__ < 2, 'not support this library')

def test_div_3(self):

result = self.cal.div()

self.assertEqual(result, 2)

@unittest.skipUnless(sys.platform.startswith('win'), 'windows')

def test_div_4(self):

result = self.cal.div()

self.assertEqual(result, 2)

@unittest.skipUnless(sys.platform.startswith('linux'), 'linux')

def test_div_5(self):

result = self.cal.div()

self.assertEqual(result, 2)

def suite1():

## 执行测试用例构建条件

suite = unittest.TestSuite()

suite.addTest(CalculatorTest("test_add"))

suite.addTest(CalculatorTest("test_sub"))

return suite

def suite2():

# 指定前缀构建测试套件

suite = unittest.makeSuite(CalculatorTest, 'test')

return suite

if __name__ == "__main__":

## 运行用例方法1

#my_print("Test suit : run some testcase")

#runner = unittest.TextTestRunner()

#runner.run(suite1())

## 运行用例方法2

#my_print("Test suit : run all testcase")

#runner.run(suite2())

## 运行用例方法3

# python ./testCalculator.py -v

# python ./testCalculator.py -v testCalculator.test_add

my_print("Run all testcase dircetly")

unittest.main()

关注的点:

测试用例指定条件,在不符合条件的情况下跳过不执行(见最后几个带修饰器的用例,对于跨平台什么实用)

所有测试用例执行顺序与其在类中的定义顺序没有关系,不能依靠这个先后关系;并且不同用例之间最好不要相互依赖。

如上,运行所有测试用例,或者指定某个测试用例运行。

$ python testCalculator.py -v

test_add (__main__.CalculatorTest) ... ok

test_div (__main__.CalculatorTest) ... ok

test_div_1 (__main__.CalculatorTest) ... skipped 'just skip'

test_div_2 (__main__.CalculatorTest) ... skipped 'not support this library'

test_div_3 (__main__.CalculatorTest) ... ok

test_div_4 (__main__.CalculatorTest) ... skipped 'windows'

test_div_5 (__main__.CalculatorTest) ... ok

test_mul (__main__.CalculatorTest) ... ok

test_sub (__main__.CalculatorTest) ... ok

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

Ran 9 tests in 0.001s

OK (skipped=3)

$ python testCalculator.py -v CalculatorTest.test_add

test_add (__main__.CalculatorTest) ... ok

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

Ran 1 test in 0.001s

OK

多个测试套件运行

$ python -m unittest discover

### 结合一下参数, python自动匹配运行所有符合条件的测试用例

-v, --verbose

Verbose output

-s, --start-directory directory

Directory to start discovery (. default)

-p, --pattern pattern

Pattern to match test files (test*.py default)

-t, --top-level-directory directory

Top level directory of project (defaults to start directory)

或者直接新建一个文件,import 所有测试类

使用 mock

前面提到,单元测试测试最小单元,但是有时候测试,遇到测试模块需要依赖于其他模块,一个是不确定依赖的模块是否有问题;另一个是依赖模块容易构造获取,没有实现。比如网络消息分析包,需要接收网络字节之类的。

这种情况下,就需要对依赖的模块进行 mock,虚拟一个依赖模块供我们测试。

如上面的例子,假如原理的 add() 还没有实现

def add(self):

pass

测试用例类似如下, 对其进行mock

from unittest import mock

def test_add(self):

result = self.cal.add()

self.assertNotEqual(result, 12)

self.cal.add = mock.Mock(return_value=12)

result = self.cal.add()

self.assertEqual(result, 12)

大概的意思,详细根据实际需求了解使用

参考 python mock

--

参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值