2011-10-05 31 views
41

我有一個類,我正在測試哪些作爲依賴另一個類(其實例傳遞給CUT的init方法)。我想用Python Mock庫嘲笑這個類。Python模擬對象與多次調用方法

我所擁有的是一樣的東西:

mockobj = Mock(spec=MyDependencyClass) 
mockobj.methodfromdepclass.return_value = "the value I want the mock to return" 
assertTrue(mockobj.methodfromdepclass(42), "the value I want the mock to return") 

cutobj = ClassUnderTest(mockobj) 

這很好,但「methodfromdepclass」是一個參數化方法,因此我想創造一個根據傳遞什麼參數的單一模仿的對象methodfromdepclass它返回不同的值。

我想要這個參數化行爲的原因是我想要創建多個包含不同值的值的實例(其值從mockobj返回什麼產生)。

還挺我在想什麼(這當然不工作):

mockobj = Mock(spec=MyDependencyClass) 
mockobj.methodfromdepclass.ifcalledwith(42).return_value = "you called me with arg 42" 
mockobj.methodfromdepclass.ifcalledwith(99).return_value = "you called me with arg 99" 

assertTrue(mockobj.methodfromdepclass(42), "you called me with arg 42") 
assertTrue(mockobj.methodfromdepclass(99), "you called me with arg 99") 

cutinst1 = ClassUnderTest(mockobj, 42) 
cutinst2 = ClassUnderTest(mockobj, 99) 

# now cutinst1 & cutinst2 contain different values 

如何實現這個「ifcalledwith」之類語義?

回答

60

嘗試side_effect

def my_side_effect(*args, **kwargs): 
    if args[0] == 42: 
     return "Called with 42" 
    elif args[0] == 43: 
     return "Called with 43" 
    elif kwarg['foo'] == 7: 
     return "Foo is seven" 

mockobj.mockmethod.side_effect = my_side_effect 
+4

太棒了,正是我所需要的。不是語法的粉絲,但是很棒。謝謝! –

+1

在使用這個解決方案時,我想在這裏添加一個警告,這很適用,順便說一句,如果您打算將異常作爲副作用,則必須提升它們,而不是返回它們。模擬庫很適合讓你給side_effect指定一個例外,然後用這個方法來DIY。 – ThatsAMorais

8

我碰到這個時候我在做我自己的測試。如果你不關心捕獲調用您的methodfromdepclass(),但只需要它返回的東西,那麼以下就足夠了:

def makeFakeMethod(mapping={}): 
    def fakeMethod(inputParam): 
     return mapping[inputParam] if inputParam in mapping else MagicMock() 
    return fakeMethod 

mapping = {42:"Called with 42", 59:"Called with 59"} 
mockobj.methodfromdepclass = makeFakeMethod(mapping) 

這裏有一個參數版本:

def makeFakeMethod(): 
    def fakeMethod(param): 
     return "Called with " + str(param) 
    return fakeMethod 
+1

+1:比'side_effect'的if-block模式更嚴格的語法,可能會返回Mock()而不是none,這樣默認的行爲就會被維護? –

+0

感謝您的建議。將它更新爲返回MagicMock()。 – Addison

39

有點甜:

mockobj.method.side_effect = lambda x: {123: 100, 234: 10000}[x] 

或多個參數:

mockobj.method.side_effect = lambda *x: {(123, 234): 100, (234, 345): 10000}[x] 

或默認值:

mockobj.method.side_effect = lambda x: {123: 100, 234: 10000}.get(x, 20000) 

或兩者的組合:高才好

mockobj.method.side_effect = lambda *x: {(123, 234): 100, (234, 345): 10000}.get(x, 20000) 

和歡快。

+0

小巧簡潔,但我喜歡。如果'lambda'返回一個'Mock'實例,以便未計數的行爲恢復到'mock'庫的正常行爲呢? –