• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • vbs
  • DOS/BAT
  • hta/htc
  • python
  • perl
  • VBA
  • ColdFusion
  • ruby
  • PowerShell
  • Lua
  • Golang
  • linux shell
您的位置:首页 > 脚本语言 >python > 利用Python中的mock库对Python代码进行模拟测试

利用Python中的mock库对Python代码进行模拟测试

作者:goldensun 字体:[增加 减小] 来源:互联网

goldensun 通过本文主要向大家介绍了python mock,mock,tdx_mock.exe,mock up,mock up是什么意思等相关知识,希望对您有所帮助,也希望大家支持linkedu.com www.linkedu.com

 如何不靠耐心测试

通常,我们编写的软件会直接与那些我们称之为“肮脏的”服务交互。通俗地说,服务对我们的应用来说是至关重要的,它们之间的交互是我们设计好的,但这会带来我们不希望的副作用——就是那些在我们自己测试的时候不希望的功能。

比如,可能我们正在写一个社交软件并且想测试一下“发布到Facebook的功能”,但是我们不希望每次运行测试集的时候都发布到Facebook上。

Python的unittest库中有一个子包叫unittest.mock——或者你把它声明成一个依赖,简化为mock——这个模块提供了非常强大并且有用的方法,通过它们可以模拟或者屏敝掉这些不受我们希望的方面。
 注意:mock是最近收录在Python 3.3标准库中的;之前发布的版本必须通过 PyPI下载Mock库。

恐惧系统调用

再举一个例子,考虑系统调用,我们将在余下的文章中讨论它们。不难发现,这些都可以考虑使用模拟:无论你是想写一个脚本弹出一个CD驱动,或者是一个web服务用来删除/tmp目录下的缓存文件,或者是一个socket服务来绑定一个TCP端口,这些调用都是在你单元测试的时候是不被希望的方面。

作为一个开发人员,你更关心你的库是不是成功的调用了系统函数来弹出CD,而不是体验每次测试的时候CD托盘都打开。

作为一个开发人员,你更关心你的库是不是成功调用了系统函数来弹出CD(带着正确的参数等)。而不是体验每次测试的时候CD托盘都打开(或者更糟,很多次,当一个单元测试运行的时候,很多测试点都涉及到了弹出代码)。

同样地,保持你的单元测试效率和性能意味着要还要保留一些自动化测试之外的“缓慢代码”,比如文件系统和网络的访问。

对于我们的第一个例子,我们要重构一个从原始到使用mock的一个标准Python测试用例。我们将会证明如何用mock写一个测试用例使我们的测试更智能、更快,并且能暴露更多关于我们的软件工作的问题。

一个简单的删除功能

有时,我们需要从文件系统中删除文件,因此,我们可以写这样的一个函数在Python中,这个函数将使它更容易成为我们的脚本去完成这件事情。
 

#!/usr/bin/env python# -*- coding: utf-8 -*-import osdef rm(filename):
  os.remove(filename)
</div>

很明显,在这个时间点上,我们的rm方法不提供比基本os.remove方法更多的功能,但我们的代码将会有所改进,允许我们在这里添加更多的功能。

让我们写一个传统的测试用例,即,不用模拟测试:
 

#!/usr/bin/env python# -*- coding: utf-8 -*-from mymodule import rmimport os.pathimport tempfileimport unittestclass RmTestCase(unittest.TestCase):
 
  tmpfilepath = os.path.join(tempfile.gettempdir(), "tmp-testfile")  def setUp(self):
    with open(self.tmpfilepath, "wb") as f:
      f.write("Delete me!")    
  def test_rm(self):
    # remove the file
    rm(self.tmpfilepath)    # test that it was actually removed
    self.assertFalse(os.path.isfile(self.tempfile), "Failed to remove the file.")
</div>

我们的测试用例是相当简单的,但当它每次运行时,一个临时文件被创建然后被删除。此外,我们没有办法去测试我们的rm方法是否传递参数到os.remove中。我们可以假设它是基于上面的测试,但仍有许多需要被证实。

重构与模拟测试

让我们使用mock重构我们的测试用例:
 

#!/usr/bin/env python# -*- coding: utf-8 -*-from mymodule import rmimport mockimport unittestclass RmTestCase(unittest.TestCase):
   
  @mock.patch('mymodule.os')
  def test_rm(self, mock_os):
    rm("any path")    # test that rm called os.remove with the right parameters
    mock_os.remove.assert_called_with("any path")
</div>

对于这些重构,我们已经从根本上改变了该测试的运行方式。现在,我们有一个内部的对象,让我们可以使用另一个功能验证。
潜在的陷阱

第一件要注意的事情就是,我们使用的mock.patch方法的装饰位于mymodule.os模拟对象,并注入到我们测试案例的模拟方法。是模拟os更有意义,还是它在mymodule.os的参考更有意义?

当然,当Python出现在进口和管理模块时,用法是非常的灵活。在运行时,该mymodule模块有自己的os操作系统——被引入到自己的范围内的模块。因此,如果我们模拟os系统,我们不会看到模拟测试在mymodule模块的影响。

这句话需要深刻的记住:

   

如果你需要为myproject.app.MyElaborateClass模拟tempfile模型,你可能需要去模拟myproject.app.tempfile的每个模块来保持自己的进口。

这就是用陷阱的方式来模拟测试。

向‘rm'中加入验证

之前定义的 rm 方法相当的简单 . 在盲目的删除之前,我们会拿它来验证一个路径是否存在,并验证其是否是一个文件. 让我们重构 rm 使其变得更加聪明:
 

#!/usr/bin/env python# -*- coding: utf-8 -*-import osimport os.pathdef rm(filename):
  if os.path.isfile(filename):
    os.remove(filename)
</div>

很好. 现在,让我们调整我们的测试用例来保持测试的覆盖程度.
 

#!/usr/bin/env python# -*- coding: utf-8 -*-from mymodule import rmimport mockimport unittestclass RmTestCase(unittest.TestCase):
   
  @mock.patch('mymodule.os.path')
  @mock.patch('mymodule.os')
  def test_rm(self, mock_os, mock_path):
    # set up the mock
    mock_path.isfile.return_value = False
     
    rm("any path")    
    # test that the remove call was NOT called.
    self.assertFalse(mock_os.remove.called, "Failed to not remove the file if not present.")    
    # make the file 'exist'
    mock_path.isfile.return_value = True
     
    rm("any path")
     
    mock_os.remove.assert_called_with("any path")
</div>

我们的测试范例完全变化了. 现在我们可以核实并验证方法的内部功能是否有任何副作用.

将删除功能作为服务

到目前为止,我们只是对函数功能提供模拟测试,并没对需要传递参数的对象和实例的方法进行模拟测试。接下来我们将介绍如何对对象的方法进行模拟测试。

首先,我们先将rm方法重构成一个服务类。实际上将这样一个简单的函数转换成一个对象并不需要做太多的调整,但它能够帮助我们了解mock的关键概念。下面是重构的代码:
 

#!/usr/bin/env python# -*- coding: utf-8 -*-import osimport os.pathclass RemovalService(object):
  """A service for removing objects from the filesystem."""
 
  def rm(filename):
    if os.path.isfile(filename):
      os.remove(filename)
</div>

你可以发现我们的测试用例实际上没有做太多的改变:
 

#!/usr/bin/env python# -*- coding: utf-8 -*-from mymodule import RemovalServiceimport mockimport unittestclass RemovalServiceTestCase(unittest.TestCase):
   
  @mock.patch('mymodule.os.path')
  @mock.patch('mymodule.os')
  def test_rm(self, mock_os, mock_path):
    # instantiate our service
    reference = RemovalService()    
    # set up the mock
    mock_path.isfile.return_value = False
     
    reference.rm("any path")    
    # test that the remove call was NOT called.
    self.assertFalse(mock_os.remove.called, "Failed to not remove the file if not present.")    
    # make the file 'exist'
    mock_path.isfile.return_value = True
     
    reference.rm("any path")
     
    mock_os.remove.assert_called_with("any path")
</div>

很好,RemovalService如同我们计划的一样工作。接下来让我们创建另一个以该对象为依赖项的服务:
 

#!/usr/bin/env python# -*- coding: utf-8 -*-import osimport os.pathclass RemovalService(object):
  """A service for removing objects from the filesystem."""
 
  def rm(filename):
    if os.path.isfile(filename):
      os.remove(filename)      
 
class UploadService(object):
 
  def __init__(self, removal_service):
    self.removal_service = remov
  


 

您可能想查找下面的文章:

  • 利用Python中的mock库对Python代码进行模拟测试

相关文章

  • Python中MYSQLdb出现乱码的解决方法
  • 详解Python中contextlib上下文管理模块的用法
  • python操作ie登陆土豆网的方法
  • python套接字流重定向实例汇总
  • 利用Python画ROC曲线和AUC值计算
  • 图文讲解选择排序算法的原理及在Python中的实现
  • python中解析json格式文件的方法示例
  • Python网络编程中urllib2模块的用法总结
  • 浅谈Python类的__getitem__和__setitem__特殊方法
  • Python使用稀疏矩阵节省内存实例

文章分类

  • vbs
  • DOS/BAT
  • hta/htc
  • python
  • perl
  • VBA
  • ColdFusion
  • ruby
  • PowerShell
  • Lua
  • Golang
  • linux shell

最近更新的内容

    • 跟老齐学Python之关于循环的小伎俩
    • 深入解析Python中的WSGI接口
    • python简单实现旋转图片的方法
    • Python for Informatics 第11章 正则表达式(一)
    • python 域名分析工具实现代码
    • 初学Python函数的笔记整理
    • python单例模式实例分析
    • Linux系统上Nginx+Python的web.py与Django框架环境
    • 使用python Django做网页
    • 从零学python系列之从文件读取和保存数据

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有