有时,测试需要调用依赖于全局设置的功能,或调用不易测试的代码,如网络访问。
使用 monkeypatch 夹具可以安全地设置/删除属性、字典项或环境变量,或修改 sys.path 以进行导入。
请求的测试功能或夹具完成后,所有修改都将被撤消。
通过写了一个demo,对官方文档进行补充。
使用monkeypatch实现模拟类时,需直接替换目标文件中引入的工具类,而不是在目标文件中替换目标类中实例化后的工具类变量。
原函数
初始化了一个ssh工具类文件:ssh_tools.py
,与调用了这个工具类的文件demo_module.py
ssh_tools.py
#! /usr/bin/env python3
# -*- coding:utf-8 -*-
# ------------------------------ #
# 原函数,被引用的ssh工具类的函数
# ------------------------------ #
class SshCom():
def __init__(self,server_info) -> None:
pass
pass
demo_module.py
#! /usr/bin/env python3
# _*_ coding : UTF-8 _*_
# ------------------------------ #
# 调用了ssh工具的函数
# ------------------------------ #
from ssh_tools import SshCom
class LinuxInfo():
def __init__(
self,
server_info: dict
) -> None:
self.ssh_com = SshCom(server_info)
@property
def get_card_drive_lsmod(self):
card_drive_lsmod = self.ssh_com.ssh_get_response(
"lsmod | grep -i nvidia"
)
if card_drive_lsmod:
return True
else:
return False
monkeypatch实现模拟类
有两种实现方式,均可实现用monkeypatch替换原工具类函数,实现不连接网络也可以进行单元测试。
我这里框架使用的pytest,所以优先使用monkeypatch
test.pytest.py
#! /usr/bin/env python3
# -*- coding:utf-8 -*-
# ------------------------------ #
# 使用 pytest 单元测试函数
# 实现在不方便通过网络调试代码时,
# 使用monkeypatch替换,被测函数中,使用到网络的工具类
# 安全地设置/删除属性、字典项或环境变量,或进行修改sys.path以进行导入。
# ------------------------------ #
import pytest
def test_get_card_drive_lsmod_demo1(monkeypatch):
"""第一种方法替换需要被测试的函数文件中引入SshCom
替换目标文件中被导入的类,替换为模拟类。
:param object monkeypatch: 用于模拟环境的补丁模块
:return bool: 通过或不能通过
"""
class MockSshCom():
def __init__(self,server_info) -> None:
pass
def ssh_get_response(self,command):
if "nvidia" in command:
return True
else:
return False
import demo_module
monkeypatch.setattr(demo_module,"SshCom",MockSshCom)
linux_info = demo_module.LinuxInfo(server_info={})
assert linux_info.get_card_drive_lsmod == True
def test_get_card_drive_lsmod_demo2(monkeypatch):
"""第二种方法替换需要被测试的函数文件中引入SshCom
为了方便起见,可以指定一个字符串,该字符串将被解释为点分导入路径,最后一部分是属性名称:
这里是替换为模拟类。
:param object monkeypatch: 用于模拟环境的补丁模块
:return bool: 通过或不能通过
"""
class MockSshCom():
def __init__(self,server_info) -> None:
pass
def ssh_get_response(self,command):
if "nvidia" in command:
return True
else:
return False
from demo_module import LinuxInfo
monkeypatch.setattr("demo_module.SshCom",MockSshCom)
linux_info = LinuxInfo(server_info={})
assert linux_info.get_card_drive_lsmod == True
unitest
也可以在pytest的用例中,使用uniitest.mock的方式,只不过不太推荐
test_unittest.py
#! /usr/bin/env python3
# -*- coding:utf-8 -*-
# ------------------------------ #
# pytest的测试用例。
# 使用unittest.mock实现monkeypatch功能
# 替换被测函数中,使用到网络的工具类
# 可以跑通
# ------------------------------ #
import demo_module
class LinuxInfo():
def __init__(self,server_info:dict,ssh_com=None) -> None:
self.ssh_com = ssh_com
@property
def get_card_drive_lsmod(self):
card_drive_lsmod = self.ssh_com.ssh_get_response("lsmod | grep -i nvidia")
if card_drive_lsmod:
return True
else:
return False
import unittest
from unittest import mock
class TestLinuxInfo(unittest.TestCase):
def test_get_card_drive_lsmod(self):
mock_ssh_com = mock.Mock()
linux_info = LinuxInfo(server_info={},ssh_com = mock_ssh_com)
mock_ssh_com.ssh_get_response.return_value = "nvidia"
result = linux_info.get_card_drive_lsmod
self.assertTrue(result)
mock_ssh_com.ssh_get_response.return_value = ""
result = linux_info.get_card_drive_lsmod
self.assertFalse(result)
也可以用pytest的夹具,来调用unittest.mock
test_fixture.py
#! /usr/bin/env python3
# -*- coding:utf-8 -*-
# ------------------------------ #
# pytest的测试用例。
# 使用unittest.mock实现monkeypatch功能
# 替换被测函数中,使用到网络的工具类
# 在这里用到了夹具来引入unittest.mock
# ------------------------------ #
import pytest
from unittest import mock
from ssh_tools import SshCom
class LinuxInfo():
def __init__(
self,
server_info: dict,
ssh_com = None
) -> None:
self.ssh_com = ssh_com
@property
def get_card_drive_lsmod(self):
card_drive_lsmod = self.ssh_com.ssh_get_response(
"lsmod | grep -i nvidia"
)
if card_drive_lsmod:
return True
else:
return False
@pytest.fixture
def mock_ssh_com():
return mock.Mock()
def test_get_card_drive_lsmod(mock_ssh_com):
linux_info = LinuxInfo(server_info={},ssh_com = mock_ssh_com)
mock_ssh_com.ssh_get_response.return_value = "nvidia"
result = linux_info.get_card_drive_lsmod
assert result
mock_ssh_com.ssh_get_response.return_value = ""
result = linux_info.get_card_drive_lsmod
assert not result
拓展
除了monkeypatch,unittest.mock以外,还有MagicMock等方法,这个就到后面有需求的时候再研究了。
参考:
[ 三年后的杂谈 ] 测试之Mock
How to monkeypatch/mock modules and environments
版权属于:寒夜方舟
本文链接:https://www.wnark.com/archives/254.html
本站所有原创文章采用署名-非商业性使用 4.0 国际 (CC BY-NC 4.0)。 您可以自由地转载和修改,但请注明引用文章来源和不可用于商业目的。声明:本博客完全禁止任何商业类网站转载,包括但不限于CSDN,51CTO,百度文库,360DOC,AcFun,哔哩哔哩等网站。