文章目录
- 前言
- 环境准备
- unittest
- pytest
- pytest插件
- mock
- 最后
前言
例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。
Python测试应用与公具 今天跟大家分享一个Python与测试相关的话题,主要介绍Python中的标准库
unittest
及第三方测试工具pytest
及mock
。介绍了它们的基本使用。
环境准备
- 一台Windows机器,或一台Linux机器,亦或一台Mac
- 安装Python,版本为2.7.x或3.5.x
- 要有pip工具(Python2),Python3不做要求
unittest
unittest是Python标准库中用于单元测试的模块。单元测试用来对最小可测试单元进行正确性检验,帮助我们在上线之前发现问题。
接下来我们通过测试collections模块中的Counter类,先来了解unittest的用法。大家或许对collections库中Counter类不太熟悉,为了让大家更好地理解这个例子,这里简单介绍一下Counter的使用。
>>> from collections import Counter
>>> c = Counter('abcdaba') # 用来计算字符串abcdaba中各个字符出现的次数
>>> c.keys()
dict_keys(['a', 'b', 'c', 'd'])
>>> c.values()
dict_values([3, 2, 1, 1])
>>> c.elements()
<bound method Counter.elements of Counter({'a': 3, 'b': 2, 'c': 1, 'd': 1})>
>>> c['a']
3
>>> c['b']
2
>>> c['c']
1
>>> c['d']
1
一个测试脚本:
# filename: ut_case.py
import unittest
from collections import Counter
class TestCounter(unittest.TestCase):
def setUp(self):
self.c = Counter('abcdaba')
print('setUp starting...')
def test_basics(self):
c = self.c
self.assertEqual(c, Counter(a=3, b=2, c=1, d=1))
self.assertIsInstance(c, dict)
self.assertEqual(len(c), 4)
self.assertIn('a', c)
self.assertNotIn('f', c)
self.assertRaises(TypeError, hash, c)
def test_update(self):
c = self.c
c.update(f=1)
self.assertEqual(c, Counter(a=3, b=2, c=1, d=1, f=1))
c.update(a=10)
self.assertEqual(c, Counter(a=13, b=2, c=1, d=1, f=1))
def tearDown(self):
print('tearDown starting...')
if __name__ == '__main__':
unittest.main()
setUp
方法列出了测试前的准备工作,常用来做一些初始化的工作,非必需方法。tearDown
方法列出了测试完成后的收尾工作,用来销毁测试过程中产生的影响,也是非必需方法。TestCase
,顾名思义表示测试用例,一个测试用例可以包含多个测试方法,每个测试方法都要以test_
开头。测试方法中用到的self.assertXXX
方法是断言语句,单元测试都是使用这样的断言语句判断测试是否通过的:如果断言为False
,会抛出AssertionError
异常,测试框架就会认为此测试用例测试失败。
运行一下上面的脚本:
(venv) C:\Users\LavenLiu\IdeaProjects\TestOps>python ut_case.py
setUp starting...
tearDown starting...
.setUp starting...
tearDown starting...
.
----------------------------------------------------------------------
Ran 2 tests in 0.003s
OK
可以看到每次执行test_
开头的方法时,都会执行setUp
和tearDown
。
pytest
Python标准库提供的测试模块功能相对单一,所以在项目中通常会额外使用第三方的测试工具。这里我们介绍pytest,pytest除了比Python标准的单元测试模块unittest更简洁和高效外,还有如下特点:
- 容易上手,入门简单,官方文档有很多实例可供参考。
- 可以自动发现需要测试的模块和函数。
- 支持运行由nose、unittest等模块编写的测试用例。
- 有很多第三方插件,并且可以方便地自定义插件。
- 很容易与持续集成工具结合。
- 可以细粒度地控制要测试的测试用例。
我们要先安装pytest库:
pip install pytest
接下来演示pytest常用的测试方法。一个测试用例:
# filename: test_pytest.py
import pytest
@pytest.fixture # 创建测试环境,可以用来做setUp和tearDown的工作
def setup_math():
import math
return math
@pytest.fixture(scope='function')
def setup_function(request):
def teardown_function():
print('teardown_function called.')
request.addfinalizer(teardown_function) # 这个内嵌函数做tearDown工作
print('setup_function called.')
def test_func(setup_function):
print('Test_Func called.')
def test_setup_math(setup_math):
# pytest不需要使用self.assertXXX这样的方法,直接使用Python内置的assert断言语句即可
assert setup_math.pow(2, 3) == 8.0
class TestClass(object):
def test_in(self):
assert 'h' in 'hello'
def test_two(self, setup_math):
assert setup_math.ceil(10) == 10.0
def raise_exit():
raise SystemExit(1)
def test_mytest():
with pytest.raises(SystemExit):
raise_exit()
@pytest.mark.parametrize('test_input, expected', [
('1+3', 4),
('2*4', 8),
('1==2', False),
]) # parametrize可以用装饰器的方式集成多组测试用例
def test_eval(test_input, expected):
assert eval(test_input) == expected
unittest必须把测试放在TestCase类中,pytest只要求测试函数或者类以test开头即可。运行一下上面的脚本:
(venv) C:\Users\LavenLiu\IdeaProjects\TestOps>py.test test_pytest.py
============================= test session starts =============================
platform win32 -- Python 3.6.3, pytest-3.2.3, py-1.4.34, pluggy-0.4.0
rootdir: C:\Users\LavenLiu\IdeaProjects\TestOps, inifile:
plugins: xdist-1.20.1, random-0.2, metadata-1.5.0, instafail-0.3.0, html-1.16.0, forked-0.2
collected 12 items
test_pytest.py ............
========================== 12 passed in 0.05 seconds ==========================
测试通过,我们让其中一个测试用例测试失败:
(venv) C:\Users\LavenLiu\IdeaProjects\TestOps>py.test test_pytest.py
============================= test session starts =============================
platform win32 -- Python 3.6.3, pytest-3.2.3, py-1.4.34, pluggy-0.4.0
rootdir: C:\Users\LavenLiu\IdeaProjects\TestOps, inifile:
plugins: xdist-1.20.1, random-0.2, metadata-1.5.0, instafail-0.3.0, html-1.16.0, forked-0.2
collected 8 items
test_pytest.py ...F....
================================== FAILURES ===================================
_____________________________ TestClass.test_two ______________________________
self = <test_pytest.TestClass object at 0x0000000003837588>
setup_math = <module 'math' (built-in)>
def test_two(self, setup_math):
> assert setup_math.ceil(10) == 11.0
E AssertionError: assert 10 == 11.0
E + where 10 = <built-in function ceil>(10)E
+ where <built-in function ceil> = <module 'math' (built-in)>.ceil
test_pytest.py:31: AssertionError
===================== 1 failed, 7 passed in 0.08 seconds ======================
pytest帮助我们定位到测试失败的位置,并告诉我们预期值和实际值。pytest的命令行功能非常丰富:
# 与使用pytest的作用一样
python -m pytest test_pytest.py
# 验证整个目录
pytest /path/to/test/dir
# 只验证文件中的单个测试用例,这在实际工作中非常方便,
# 否则可能需要运行一段时间才能轮到有问题的测试用例,极为浪费时间。
# 使用这样的方式就可以有针对性地验证有问题的测试用例
pytest test_pytest.py::test_mytest
# 只验证测试类中的单个方法
pytest test_pytest.py::TestClass::test_in
pytest插件
pytest有丰富的插件,这里列出几个常用的pytest插件,pytest插件都是以pytest-
开头。
pytest-random
:可以让测试变得随机。当有很多测试用例时,这个插件不会让测试只卡在一个异常上,有助于发现其他异常。pytest-xdist
:让pytest支持分布式测试pytest-instafail
:一旦出现错误信息就立即返回,不需要等到全部测试结束后才显示。pytest-html
:可以生存测试报告文件。
mock
Mock测试是在测试过程中对可能不稳定、有副作用、不容易构造或者不容易获取的对象,用一个虚拟的对象来创建以便完成测试的方法。在Python中,这种测试是通过第三方的mock
库完成的,mock
在Python3.3的时候被引入到了Python标准库中,改名为unittest.mock
。之前的Python版本都需要安装它:
pip install mock
假设现在一个单元测试依赖外部的API返回值。举个例子(client.py):
# filename: client.py
import requests
def api_request(url):
r = requests.get(url)
return r.json()
def get_review_author(url):
res = api_request(url)
return res['review']['author']
如果在测试时,每次都真正请求这个接口,就会有两个问题:
- 测试环境可能和线上环境不同,需要搭建本地的API服务,尤其是需要本地环境能返回线上环境实际的全部结果,增加复杂度且效率低下。
- 测试结果严重依赖外部API服务的稳定性。
使用mock
的解决方案如下(test_mock.py):
# filename: test_mock.py
import unittest
import mock
import client
class TestClient(unittest.TestCase):
def setUp(self):
self.result = {'review': {'author': 'testops'}}
def test_request(self):
api_result = mock.Mock(return_value=self.result)
client.api_request = api_result
self.assertEqual(client.get_review_author(
'http://api.testops.cn/review/123'), 'testops')
运行一下看看效果:
(venv) C:\Users\LavenLiu\IdeaProjects\TestOps>pytest test_mock_py3.py
============================= test session starts =============================
rootdir: C:\Users\LavenLiu\IdeaProjects\TestOps, inifile:
plugins: xdist-1.20.1, random-0.2, metadata-1.5.0, instafail-0.3.0, html-1.16.0, forked-0.2
collected 1 item
test_mock_py3.py .
========================== 1 passed in 0.36 seconds ===========================
可以看到,这个测试并没有实际地请求API就达到了测试的目的。
最后
分享一份完整版的Python全套学习资料
一、Python所有方向的学习路线
Python所有方向路线就是把Python常用的技术点做整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。
|
– | – |
二、学习软件 |
工欲善其事必先利其器。学习Python常用的开发软件都在这里了,给大家节省了很多时间。