1.问题
使用pytest
做python代码的测试是很容易的,不过被测试代码对网络、数据库等远程服务有依赖,这种情况该如何快速方便的进行测试。
2.方案
python的unitest包是支持单元测试的,有mock可以用。并且pytest-mock进行高层次的封装,用起来更加方便。安装包:
pip3 install pytest-mock
2.1.小试牛刀
直接采用类似官方的例子,我们代码中要使用os.getcwd
获取当前工作目录。只是我们的代码是在windows系统上,实际代码需要linux下工作,测试代码如下:
import os
def test_os_getcwd(mocker):
mocker.patch("os.getcwd", return_value="/root")
ret = os.getcwd()
assert ret == '/root'
这段代码可以测试通过。通过使用mocker.patch
可以对函数进行mock。这样就本地开发环境进行测试,这也是单元测试魅力所在。
看上去很简单对吧,实际这样例子毫无意义,实际测试根本不会这么用。网上的例子到处是这样的代码,简直是误人子弟,纯粹是浪费时间。因此才想起写一份有用的例子代码出来。
2.2.测试自定义模块
假设我们写了一个模块,有个函数get_pipconf_filename
的路径。模块chaos的代码如下:
import os
def get_pipconf_filename():
upath = os.path.expanduser('~')
return upath + '/.pip/pip.conf'
我们写下测试代码:
import chaos
def test_get_pipconf_filename(mocker):
mocker.patch('os.path.expanduser',return_value='/home/chaos')
ret = chaos.get_pipconf_filename()
assert ret == 'home/chaos/.pip/pip.conf'
问题来了,你觉得上述测试代码可以通过吗?答案是肯定的,测试通过。
不过,如果chaos模块中是另外一种写法,上述测试还能通过吗?
from os.path import expanduser
def get_pipconf_filename():
upath = expanduser('~')
return upath + '/.pip/pip.conf'
实际测试是失败,无法进行mock,原因就是patch打错对象了,实际上没有成功。测试代码该如何写?代码如下:
import chaos
def test_get_pipconf_filename(mocker):
mocker.patch('chaos.expanduser', return_value='/home/chaos')
ret = chaos.get_pipconf_filename()
assert ret == '/home/chaos/.pip/pip.conf'
这个时候应该对chaos.expanduser
进行mock才行,因为chaos模块中对os.path.expanduser
进行了重新赋值。
截止到这里,给模块函数进行mock几种场景,基本能覆盖实际的测试。但问题又来了,如何根据函数调用传递的参数不同而返回不同的值呢?代码如下:
import chaos
def mock_func(s):
if s == '~' :
return 'home/chaos'
else:
return '/usr/lib'
def test_get_pipconf_filename(mocker):
mocker.patch('chaos.expanduser', side_effect=mock_func)
ret = chaos.get_pipconf_filename('^_^')
assert ret == '/usr/lib'
上述代码使用了side_effect
参数重定向到一个自定义的测试函数上。这个时候就可以根据实际的参数进行返回不同的值了。
3.讨论
实际上,还有一些问题没有解决?比如如何对对象进行patch,如何在初始化的时候把所有的patch都打上,不用在每个测试用例进行patch等等。留在下一篇再讲。
网友评论