2016-07-26 71 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'測試的例子。 –