2016-07-26 69 views
6

假设我有这样的班级。如何提供python单元测试的模拟类方法?

class SomeProductionProcess(CustomCachedSingleTon): 

     def loaddata(): 
      """ 
      Uses an iterator over a large file in Production for the Data pipeline. 
      """ 

      pass 

现在在测试时间我想改变loaddata()方法中的逻辑。这将是一个简单的自定义逻辑,不会处理大量数据。

我们如何在测试时间使用Python Mock UnitTest框架提供loaddata()的自定义实现?

+0

请注意,loaddata()是@classmethod。另外我想提供一个自定义的loaddata()来测试这个类中的另一个方法。 – user4654

+0

考虑到这个问题,'模拟方法'会更加正确,因为当你说'class method'时,隐含了'classmethod'的模拟方法。一个方法总是一个类实例的一部分,一个classmethod是类的一部分。使用装饰器@classmethod使其正确更改方法并添加参数cls。 – msudder

回答

0

比方说你有一个名为awesome.py和它模块,您有:

import time 

class SomeProductionProcess(CustomCachedSingleTon): 

    def loaddata(self): 
     time.sleep(30) # simulating a long running process 
     return 2 

那么你的单元测试,你嘲笑loaddata看起来是这样的:

import unittest 

import awesome # your application module 


class TestSomeProductionProcess(unittest.TestCase): 
    """Example of direct monkey patching""" 

    def test_loaddata(self): 
     some_prod_proc = awesome.SomeProductionProcess() 
     some_prod_proc.loaddata = lambda x: 2 # will return 2 every time called 
     output = some_prod_proc.loaddata() 
     expected = 2 

     self.assertEqual(output, expected) 

或者它可能看起来像这样:

import unittest 
from mock import patch 

import awesome # your application module 

class TestSomeProductionProcess(unittest.TestCase): 
    """Example of using the mock.patch function""" 

    @patch.object(awesome.SomeProductionProcess, 'loaddata') 
    def test_loaddata(self, fake_loaddata): 
     fake_loaddata.return_value = 2 
     some_prod_proc = awesome.SomeProductionProcess() 

     output = some_prod_proc.loaddata() 
     expected = 2 

     self.assertEqual(output, expected) 

现在,当您运行测试时,loaddata将不会花费30秒来处理这些测试用例。

+0

非常感谢您的回复。我想提供一个需要实施的存根,而不是返回2,我该怎么做?而不是lamdba x:2 – user4654

+0

无论您放在lambda的':'的右侧,都会返回。 – willnx

+0

我如何彻底删除lambda表达式?只分配一个自定义函数。像这样:some_prod_proc = custom_func()。我担心它会完全覆盖函数定义,而不仅仅是在测试环境中。 – user4654

4

要使用结构化的return_value轻松地嘲笑类方法,可以使用unittest.mock.Mock

from unittest.mock import Mock 

mockObject = SomeProductionProcess 
mockObject.loaddata = Mock(return_value=True) 

编辑:

由于要模拟出一个自定义实现的方法,你可能只是在测试运行时创建一个自定义的模拟方法的对象和换出原来的方法。

def custom_method(*args, **kwargs): 
    # do custom implementation 

SomeProductionProcess.loaddata = custom_method 
+0

我想做一个loaddata方法的自定义实现,而不仅仅是返回值。 – user4654

+0

您可以将loaddata分配给自定义实现。给我一秒钟,我会更新我的答案。 –

5

这里有一个简单的方法用做模拟

import mock 


def new_loaddata(cls, *args, **kwargs): 
    # Your custom testing override 
    return 1 


def test_SomeProductionProcess(): 
    with mock.patch.object(SomeProductionProcess, 'loaddata', new=new_loaddata): 
     obj = SomeProductionProcess() 
     obj.loaddata() # This will call your mock method 

我推荐使用pytest代替unittest模块,如果你能。它使您的测试代码更加清洁,并减少了大量使用unittest.TestCase样式测试的样板。

+0

谢谢,所以你提到的方法是根据unittest还是pytest? – user4654

+0

'mock'可以用于任一种。使用'pytest'或'unittest'的区别只是改变了你的测试函数的样子。我的例子使用'pytest'格式,你只需要使用常规函数和内置的'assert'语句。看看@willnx [answer](http://stackoverflow.com/a/38579804/1547004),看看'unittest'测试的例子。 –