目录:

1.unittest.TestCase中常用的断言方法

  1.1 subTest子测试

  1.2 套件测试

  1.3 批量测试单个用例

2. 加载器

  2.1加载器协议

  2.2.执行器 TestRunner

3.已现成的测试函数用例

4.Mock

Mock对象的参数:

  4.1 return_value

  4.2 side_effect

  4.3 spec

  4.4 wraps

MagicMock对象额外方法

  4.5mock_add_spec方法

5. patch 用模拟对象替换真实对象

6.代码覆盖率coverage


1. unittest.TestCase类中的常用的断言方法

方法 用途
assertEqual(a, b) 核实 a == b
assertNotEqual(a, b) 核实 a != b
assertTrue(x) 核实 x 为True
assertFalse(x) 核实 x 为False
assertIn(itemlist) 核实itemlist
assertNotIn(itemlist) 核实item不在list

1.1 子测试:记录错误并测试完所有的代码

class DemoTest(unittest.TestCase):
def test_subtest(self):
for i in range(5):
with self.subTest(name=i): # 子测试参数用于输出
self.assertEqual(i % 2, 0) >>>
(djProj_py3) appledeMacBook-Air-7:tests apple$ python -m unittest test_a.DemoTest.test_subtest ======================================================================
FAIL: test_subtest (test_a.DemoTest) (name=1)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/apple/PycharmProjects/work/practice/tests/test_a.py", line 19, in test_subtest
self.assertEqual(i % 2, 0)
AssertionError: 1 != 0 ======================================================================
FAIL: test_subtest (test_a.DemoTest) (name=3)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/apple/PycharmProjects/work/practice/tests/test_a.py", line 19, in test_subtest
self.assertEqual(i % 2, 0)
AssertionError: 1 != 0 ----------------------------------------------------------------------
Ran 1 test in 0.000s FAILED (failures=2)

1.2测试套件:将多个用例或套件的实例组合起来,完成产品功能组级别的测试。

分别为每个参与测试方法创建实例,并加入套件。

class UserTest(unittest.TestCase):

    def test_user(self):
self.assertTrue(True) # 判断是否为真 class CartTest(unittest.TestCase): def test_cart(self):
self.assertFalse(False) suite = unittest.TestSuite()
suite.addTests((UserTest('test_user'), # 创建实例并加入套件suite
CartTest('test_cart'),)
)
unittest.TextTestRunner(verbosity=2).run(suite) # TextTestRunner执行器

# code end!!
>>>
(djProj_py3) appledeMacBook-Air-7:tests apple$ python test_a.py
test_user (__main__.UserTest) ... ok
test_cart (__main__.CartTest) ... ok ----------------------------------------------------------------------
Ran 2 tests in 0.000s OK

1.3 批量测试单个用例,可通过重写runTest

class TestDamo(unittest.TestCase):
def add(self):
self.assertTrue(1) def add1(self):
self.assertFalse('') def runTest(self):
tests = (self.add, self.add1)
for test in tests:
with self.subTest(t=test):
test() >>>
(djProj_py3) appledeMacBook-Air-7:tests apple$ python -m unittest -v test_a.py
runTest (test_a.TestDamo) ... ok ----------------------------------------------------------------------
Ran 1 test in 0.000s OK

2.加载器

完整的流程是:1.discover递归目录,查找所有文件名相符的模块。loadTestsFromModule 在模块内获取所有的用例类型,再通过以loadTestsFromTestCase

为用例的全部测试方法创建实例。最终,将之组合成测试套件交给执行器。

注: loadTestsFromTestCase调用了getTestCaseNames查找类型中包含 特定前缀(test)的测试方法,无则选择runTest;

  loadTestsFromModule按照加载协议(load_tests),先调用load_tests函数返回自定义测试套件。仅在没有协议实现时,才返回所用的用例类型;

   可以自己创建加载器对象或使用默认的defaultTestLoader实例。

class TestDamo(unittest.TestCase):
def test_add(self):
self.assertTrue(1) def test_add1(self):
self.assertFalse('') def runTest(self):
self.assertFalse('a') class Test1Damo(unittest.TestCase):
def runTest(self):
self.assertFalse('')
loader = unittest.defaultTestLoader
a = loader.loadTestsFromTestCase(TestDamo)
print(a)
b = loader.loadTestsFromModule(sys.modules[__name__])
print(b) >>>
(djProj_py3) appledeMacBook-Air-7:tests apple$ python test_a.py <unittest.suite.TestSuite tests=[<__main__.TestDamo testMethod=test_add>, <__main__.TestDamo testMethod=test_add1>]>
<unittest.suite.TestSuite tests=[<unittest.suite.TestSuite tests=[<__main__.Test1Damo testMethod=runTest>]>,
                    <unittest.suite.TestSuite tests=[<__main__.TestDamo testMethod=test_add>,
                                        <__main__.TestDamo testMethod=test_add1>]>
]>
# 2.1改写加载器协议
def load_tests(loader, standard_tests, pattern):
suite = unittest.TestSuite()
suite.addTests(map(TestDamo, ('test_add', 'test_add1')))
return suiteb = loader.loadTestsFromModule(sys.modules[__name__])
b = loader.loadTestsFromModule(sys.modules[__name__])
print(b)
>>> (djProj_py3) appledeMacBook-Air-7:tests apple$ python test_a.py 
# 改写加载器协议后,只加载了协议指定TestDamo用例
<unittest.suite.TestSuite tests=[<__main__.TestDamo testMethod=test_add>, <__main__.TestDamo testMethod=test_add1>]>
 

2.2.执行器TestTunner

用于接受用例或套件,执行测试并返回结果

3.已现成的测试函数用例

用FunctionTestCase包装,它本身是继承的unittest.TestCase

def test():
assert False result = unittest.FunctionTestCase(test).run()
print(result.failures) >>>
(djProj_py3) appledeMacBook-Air-7:tests apple$ python test_a.py
[(<unittest.case.FunctionTestCase tec=<function test at 0x10cc62bf8>>,
'Traceback (most recent call last):\n File "test_a.py", line 99, in test\n assert False\nAssertionError\n')]

4.Mock

Mock以__getattr__拦截被mock替换对象的属性访问,动态创建‘替换对象成员’。 且新建成员同是模拟类型,以实现链式属性设置和访问。

我对mock的理解:测试对象功能尚未完成或者依赖其他环境(例如db),可用mock替换该测试对象并指定返回结果。其作用:先完成测试逻辑,

接触开发次序依赖

4.1 return_value

设置测试对象的返回值

>>> import unittest
>>> from unittest.mock import Mock
>>> m = Mock()
>>> m.func.return_value = 1
>>> import unittest
>>> from unittest.mock import Mock
>>> m = Mock()
>>> def func(a, b):return a+b
...
>>> m.func.return_value = 99
>>> func(50, 50)
100
>>> m.func(50, 50)
99
>>> m.func(50, 50, 1)
99
结论:通过Mock对象指定测试用例返回值后,再通过Mock调用测试对象,并不会去执行而是直接返回return_value的值

4.2 side_effect

构造参数side_effect指定可调用对象,迭代器,异常。用来替代return_value返回

# side_effect指定为可调用对象
>>> m = Mock(side_effect=lambda x: x+10)
>>> m(1)
11 # side_effect指定迭代器
>>> m = Mock()
>>> m.next = Mock(side_effect=[1,2,3])
>>> m.next
<Mock name='mock.next' id=''>
>>> m.next()
1
>>> m.next()
2
>>> m.next()
3
>>> m.next()
StopIteration # side_effect指定为异常
>>> m.next = Mock(side_effect=KeyError('key error'))
>>> m.test = Mock(side_effect=KeyError('key error'))
>>> m.test()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
raise effect
KeyError: 'key error' # side_effect的可调用对象返回值为unittest.mock.DEFAULT时,实际返回值为return_value的值
>>> m = Mock(side_effect=lambda x: 100 if x>0 else unittest.mock.DEFAULT, return_value=99)
>>> m(1)
100
>>> m(0)
99
# side_effect设置为None时,返回值为return_value
>>> m.side_effect = None
>>> m(1)
99

4.3 spec

可从列表 或 某个类型提取属性名字,用以约束模拟对象mock。

# 从列表提取属性名字
>>> m = Mock(spec=['a', 'b'])
>>> m.a
<Mock name='mock.a' id=''>
>>> m.b
<Mock name='mock.b' id=''>
>>> m.c
AttributeError: Mock object has no attribute 'c' # 从类提取属性名字
>>> class A:
... a = 1
... def b(self):
... return 2
...
>>> m = Mock(spec=A)
>>> m.a
<Mock name='mock.a' id=''>
>>> m.b
<Mock name='mock.b' id=''>
>>> m.c
AttributeError: Mock object has no attribute 'c'
# spec参数并不能阻止通过赋值创建属性
>>> m.c = 3 # 创建成功
>>> m.c
3 # spec_set可以阻止赋值创建属性
>>> m = Mock(spec_set=['a'])
>>> m.a
<Mock name='mock.a' id=''>
>>> m.b
AttributeError: Mock object has no attribute 'b'
>>> m.b = 3 # 创建失败
AttributeError: Mock object has no attribute 'b' # create_autospec约束参数列表,使mock模拟的对象与测试对象参数一致
>>> m = unittest.mock.create_autospec(A, spec_set=True, instance=True)
>>> m.test()
TypeError: missing a required argument: 'a'
>>> m.test(a=1)
TypeError: missing a required argument: 'b'
>>> m.test(a=1,b=2)
<MagicMock name='mock.test()' id=''>
>>> m.test.return_value = 1 # 指定模拟对象的返回值
>>> m.test(1,2)
1 # 从lambda对象中提取参数,限制模拟对象m.test
>>> m = Mock()
>>> m.test = unittest.mock.create_autospec(lambda a,b: 2, return_value=1) # 从lambda中提取参数a, b
>>> m.test(1)
>>> m.test = unittest.mock.create_autospec(lambda a,b: 2, return_value=1)
>>> m.test(1,2)
TypeError: missing a required argument: 'b'
>>> m.test(1,2)
1

4.4 wraps参数

可以将模拟对象的访问传值传递给真是对象, 这样可以在模拟和真实对象间切换,而非删除代码

>>> class A:
... def add(self, a, b):return a+b
>>> m = Mock(spec_set=A, wraps=A())
>>> m.add(50, 50)
100
# 但是一旦设置return_value,则不再传递参数给真实对象
>>> m.add.return_value=99
>>> m.add(50, 50)
99

4.5 MagicMock

额外提供mock_add_spec方法,用于调整spec设置

# 重置spec_set的参数(True:spec_set, False:spec)
>>> m.mock_add_spec(['a'], True)
# 阻止所有属性的访问 = Mock(spec_set=[])
>>> m.mock_add_spec([], True)
# 取消spec or spec_set设置
>>> m.mock_add_spec(None)

5. patch

使用patch将真实对象替换成模拟的对象(真是对象 x + y, 模拟对象 a去替换真实对象x,变成 a + y)

测试用例为:

test_a.py

from unittest.mock import patch
import requests def logic(url):
data = requests.get(url=url)
return data.status_code

5.1 patch 上下文管理器用法

class DemoTest(unittest.TestCase):
def test_1(self):
# 设置固定数据
data = SimpleNamespace(url='https://www.baidu.com', code=200)
with patch("test_a.logic", lambda url: data.code) as m: # 将test_a.py下的logic方法 用 lambda去替换。
self.assertEqual(m(data.url), data.code) (djProj_py3) appledeMacBook-Air-7:tests apple$ python -m unittest -v test_a.py
test_1 (test_a.DemoTest) ... ok ----------------------------------------------------------------------
Ran 1 test in 0.000s OK

5.2 patch 装饰器用法

  5.2.1 @patch('requests.get')  替换为 get (get自己起的名字)

class DemoTest(unittest.TestCase):
@patch('requests.get')
def test_1(self, get):
data = SimpleNamespace(url='https://www.baidu.com', code=200)
get.return_value.status_code = data.code
self.assertEqual(logic(data.url), data.code) (djProj_py3) appledeMacBook-Air-7:tests apple$ python -m unittest -v test_a.py
test_1 (test_a.DemoTest) ... ok ----------------------------------------------------------------------
Ran 1 test in 0.001s OK

 5.2.2  将test_a.logic替换成 lambda url: 200

class DemoTest(unittest.TestCase):
@patch('test_a.logic', lambda url: 200) # arg: url, return_value: 200
def test_1(self):
data = SimpleNamespace(url='https://www.baidu.com', code=200)
self.assertEqual(logic(data.url), data.code) (djProj_py3) appledeMacBook-Air-7:tests apple$ python -m unittest -v test_a.py
test_1 (test_a.DemoTest) ... ok ----------------------------------------------------------------------
Ran 1 test in 0.000s OK

6.coverage代码覆盖率  pip install coverage

pip install coverage

(djProj_py3) appledeMacBook-Air-7:tests apple$ coverage run --source . -m unittest test_a.DemoTest  # 仅测试当前目录下的文件
.
----------------------------------------------------------------------
Ran 1 test in 0.000s OK
(djProj_py3) appledeMacBook-Air-7:tests apple$ coverage report # 测试结果生成文本
(djProj_py3) appledeMacBook-Air-7:tests apple$ coverage html  # 测试结果生成HTML文件

python-单元测试unittest的更多相关文章

  1. python单元测试unittest

    单元测试作为任何语言的开发者都应该是必要的,因为时隔数月后再回来调试自己的复杂程序时,其实也是很崩溃的事情.虽然会很快熟悉内容,但是修改和 调试将是一件痛苦的事情,如果你在修改了代码后出现问题的话,而 ...

  2. [转]python单元测试unittest

    单元测试作为任何语言的开发者都应该是必要的,因为时隔数月后再回来调试自己的复杂程序时,其实也是很崩溃的事情.虽然会很快熟悉内容,但是修改和调试将是一件痛苦的事情,如果你在修改了代码后出现问题的话,而单 ...

  3. Python单元测试unittest - 单元测试框架

    一.unittest简介 unitest单元测试框架最初是有JUnit的启发,它支持测试自动化,共享测试的设置和关闭代码,将测试聚合到集合中,以及测试与报告框架的独立性. 二.unittest相关概念 ...

  4. python单元测试unittest实例详解

    转自:http://blog.csdn.net/five3/article/details/7104466 单元测试作为任何语言的开发者都应该是必要的,因为时隔数月后再回来调试自己的复杂程序时,其实也 ...

  5. python单元测试unittest、setUp、tearDown()

    单元测试反应的是一种以测试为驱动的开发模式,最大的好处就是保证一个程序模块的行为符合我们设计的测试用例,在将来修改的时候,可以极大程度保证该模块行为仍然是正确的. 下面我编写一个Dict来,这个类的行 ...

  6. Python单元测试unittest【转自https://www.cnblogs.com/feng0815/p/8045850.html】

    [转自https://www.cnblogs.com/feng0815/p/8045850.html] Python中有一个自带的单元测试框架是unittest模块,用它来做单元测试,它里面封装好了一 ...

  7. Python单元测试--unittest(一)

    unittest模块是Python中自带的一个单元测试模块,我们可以用来做代码级的单元测试. 在unittest模块中,我们主要用到的有四个子模块,他们分别是: 1)TestCase:用来写编写逐条的 ...

  8. selenium自动化测试、Python单元测试unittest框架以及测试报告和日志输出

    部分内容来自:https://www.cnblogs.com/klb561/p/8858122.html 一.基础介绍 核心概念:test case, testsuite, TestLoder,Tex ...

  9. python单元测试-unittest

    python内部自带了一个单元测试的模块,pyUnit也就是我们说的:unittest 1.介绍下unittest的基本使用方法: 1)import unittest 2)定义一个继承自unittes ...

  10. Python单元测试unittest测试框架

    本文的主题是自动化测试框架的实现,在实现之前,先了解一下关于unittest模块的相关知识: Python中有一个自带的单元测试框架是unittest模块,用它来做单元测试,它里面封装好了一些校验返回 ...

随机推荐

  1. typedef定义数组类型

    typedef语句定义数组类型 1. 一维数组类型的定义格式 typedef <元素类型关键字><数组类型名>[<常量表达式>]; 例如: (1) typedef ...

  2. JavaScript学习总结(二、隐式类型转换、eval())

    一.(避免)隐式类型转换 console.log(false == 0);   //logs true; console.log(false === 0);   //logs false; conso ...

  3. variable 'QJsonArray array' has initializer but incomplete type

    variable "xxx" has initializer but incomplete type 编译报以上错误 分析:“xxx”对应的类型没有找到,没包含定义该变量类型的头文 ...

  4. 黄聪:C#如何使用fiddlercoreCapture监控手机APP

    1.去下载Fiddler:https://www.telerik.com/download/fiddler 2.安装Fiddler,按下图所示导出证书,导出后在桌面得到:FiddlerRoot.cer ...

  5. What is SolrCloud? (And how does it compare to master-slave?)

    What is SolrCloud? (And how does it compare to master-slave?) SolrCloud is a set of new features and ...

  6. MySQL面试题和答案

    Mysql 的存储引擎,myisam和innodb的区别. 答: 1.MyISAM 是非事务的存储引擎,适合用于频繁查询的应用.表锁,不会出现死锁,适合小数据,小并发. 2.innodb是支持事务的存 ...

  7. 服务网关zuul之六:Zuul高可用

    我们实际使用Zuul的方式如上图,不同的客户端使用不同的负载将请求分发到后端的Zuul,Zuul在通过Eureka调用后端服务,最后对外输出.因此为了保证Zuul的高可用性,前端可以同时启动多个Zuu ...

  8. [C#][Report]Cry

    本文来自:https://wiki.scn.sap.com/wiki/display/BOBJ/Crystal+Reports%2C+Developer+for+Visual+Studio+Downl ...

  9. 各种http报错的报错的状态码的分析

    HTTP常见错误 HTTP 错误 400 400 请求出错 由于语法格式有误,服务器无法理解此请求.不作修改,客户程序就无法重复此请求. HTTP 错误 401 401.1 未授权:登录失败 此错误表 ...

  10. arduino mega 避障报距小车

    流程图 硬件 mega2560 // Pin 13 has an LED connected on most Arduino boards. // give it a name: #include&l ...