Article Outline
mock 功效
<!--more-->
完成功能测试
# module.py
class Count():
def add(self, a, b):
return a + b
# test.py
from unittest import mock
import unittest
from module import Count
class MockDemo(unittest.TestCase):
def test_add(self):
count = Count()
count.add = mock.Mock(return_value=13, side_effect=count.add)
# side_effect参数和return_value是相反的。它给mock分配了可替换的结果,覆盖了return_value。简单的说,一个模拟工厂调用将返回side_effect值,而不是return_value;所以,设置side_effect参数为Count类add()方法,那么return_value的作用失效。
result = count.add(8, 8)
print(result)
# 将会真正的调用add()方法,得到的返回值为16(8+8)。通过print打印结果。
count.add.assert_called_with(8, 8)
# 检查mock方法是否获得了正确的参数。
self.assertEqual(result, 16)
if __name__ == '__main__':
unittest.main()
解决测试依赖
例如,我们要测试A模块,然后A模块依赖于B模块的调用。但是,由于B模块的改变,导致了A模块返回结果的改变,从而使A模块的测试用例失败。其实,对于A模块,以及A模块的用例来说,并没有变化,不应该失败才对。这个时候就是mock发挥作用的时候了。通过mock模拟掉影响A模块的部分(B模块)。至于mock掉的部分(B模块)应该由其它用例来测试。
add_and_multiply()函数依赖了multiply()函数的返回值
function.py
def add_and_multiply(x, y): addition = x + y multiple = multiply(x, y) return (addition, multiple)
def multiply(x, y): return x * y
test.py
import unittest import function
class MyTestCase(unittest.TestCase):
def test_add_and_multiply(self):
x = 3
y = 5
addition, multiple = function.add_and_multiply(x, y)
self.assertEqual(8, addition)
self.assertEqual(15, multiple)
if name == "main": unittest.main()
> 修改multiply()函数的代码,这样测试会失败
```python
def multiply(x, y):
return x * y + 3
把 multiply()函数mock掉,解决依赖
import unittest from unittest.mock import patch import function
class MyTestCase(unittest.TestCase):
@patch("function.multiply")
# patch()装饰/上下文管理器可以很容易地模拟类或对象在模块测试。在测试过程中,您指定的对象将被替换为一个模拟(或其他对象),并在测试结束时还原。这里模拟function.py文件中multiply()函数。
def test_add_and_multiply2(self, mock_multiply):
# 在定义测试用例中,将mock的multiply()函数(对象)重命名为 mock_multiply对象。
x = 3
y = 5
mock_multiply.return_value = 15
# 设定mock_multiply对象的返回值为固定的15。
addition, multiple = function.add_and_multiply(x, y)
mock_multiply.assert_called_once_with(3, 5)
# 检查mock_multiply方法的参数是否正确。
self.assertEqual(8, addition)
self.assertEqual(15, multiple)
if name == "main": unittest.main()
### Mock attributes in Python mock?
```python
with patch('requests.post') as patched_post:
type(patched_post.return_value).ok = PropertyMock(return_value=True)
Mock session in requests library?
# main.py
import requests
def do_session_get():
session = requests.session()
return session.get('foo').status_code
# tests.py
import unittest
import mock
from main import do_session_get
# The module of we mock, return the exact data that we expected
def mocked_requests(*args, **kwargs):
# print args
class MockResponse(object):
def __init__(self, status_code, text, json_data=None, content=None):
self.json_data = json_data
self.status_code = status_code
self.text = text
self.content = content
def json(self):
return self.json_data
if args[0] == 'foo':
return MockResponse(200, 'success')
elif args[0] == 'http://someurl.com/':
return MockResponse(200, 'success')
return MockResponse(404, '-1')
# from main import do_session_get
class TestDoSessionGet(unittest.TestCase):
@mock.patch('requests.session')
def test_should_mock_session_get(self, mocked_session):
mocked_session.return_value = mock.MagicMock(get=mock.MagicMock(side_effect=mocked_requests))
# print do_session_get()
self.assertEqual(do_session_get(), 200)
if __name__ == '__main__':
unittest.main()
assert 常用断言速查
项目 | 举例 |
---|---|
assertEqual(a, b) | a == b |
assertNotEqual(a, b) | a != b |
assertGreater(a, b) | a > b |
assertGreaterEqual(a, b) | a >= b |
assertLess(a, b) | a < b |
assertLessEqual(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) |
干货:mock模板(requests)
# common_test.py
# -*- coding: utf-8 -*-
import unittest
import mock
import requests
"""
If the module is import from others files, like:
from my.great.package import FlowType
Then, we should do this, below:
# Now we must patch 'my.great.package.requests.get'
@mock.patch('my.great.package.requests.get', side_effect=mocked_requests)
instead of "@mock.patch('common_test.requests.get', side_effect=mocked_requests)"
because the requests module was imported in that file
"""
# The module that need unittest
class FlowType(object):
def __init__(self):
pass
def flow_type(self):
url = 'http://chihweihsu.com/'
resp = requests.get(url)
return resp
# The data of we mock
mock_status_code = 200
mock_text = 'You are my sunshine!'
mock_json = {'data': 'You are my sunshine!'}
mock_content = 'You are my sunshine!'
# The module of we mock, return the exact data that we expected
def mocked_requests(*args, **kwargs):
class MockResponse(object):
def __init__(self, status_code, text, json_data=None, content=None):
self.json_data = json_data
self.status_code = status_code
self.text = text
self.content = content
def json(self):
return self.json_data
if args[0] == 'http://chihweihsu.com/':
return MockResponse(mock_status_code, mock_text, json_data=mock_json, content=mock_content)
elif args[0] == 'http://someurl.com/':
return MockResponse(200, 'success')
return MockResponse(404, '-1')
# The whole test case module
class TestCommon(unittest.TestCase):
def setUp(self):
pass
# We patch 'common_test.requests.get' with our own method. The mock object is passed in to our test case method.
@mock.patch('common_test.requests.get', side_effect=mocked_requests)
def test_flow_type(self, mocked_get):
f = FlowType()
resp = f.flow_type()
self.assertIsNotNone(resp)
self.assertEqual(resp.status_code, mock_status_code)
self.assertIs(resp.text, mock_text)
self.assertIn(resp.content, mock_content)
self.assertDictEqual(resp.json(), mock_json)
self.assertIsInstance(resp.json(), dict)
if __name__ == '__main__':
unittest.main()