2011-10-05 12 views
41

Mam klasę, którą testuję, która ma jako zależność inną klasę (której wystąpienie zostaje przekazane do metody init CUT). Chcę wyśmiewać tę klasę przy użyciu biblioteki Python Mock.Python Mock object z metodą o nazwie multiple time

Co mam coś takiego jak:

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) 

co jest w porządku, ale „methodfromdepclass” jest metodą parametryzowane i jako takie Chcę utworzyć jeden atrapa obiektu, gdzie w zależności od tego, co argumenty są przekazywane do methodfromdepclass zwraca różne wartości.

Powodem, dla którego chcę tego sparametryzowanego zachowania, chcę utworzyć wiele wystąpień ClassUnderTest, które zawierają różne wartości (wartości których są wytwarzane przez to, co jest zwracane z mockobj).

Kinda, co mam na myśli (to oczywiście nie działa):

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 

Jak mogę osiągnąć ten „ifcalledwith” rodzaj semantyki?

Odpowiedz

60

Spróbuj 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

Niesamowite, dokładnie to, czego potrzebowałem. Nie jest fanem składni, ale działa świetnie. Dzięki! –

+1

Jedno zastrzeżenie, które chciałbym dodać tutaj, do którego uciekłem podczas korzystania z tego rozwiązania, które działa bardzo dobrze, przy okazji, jest to, że jeśli masz wyjątki jako efekty uboczne, musisz je podnosić, zamiast je zwracać. Biblioteka Mock jest dobra w pozwalaniu ci przypisać wyjątek do side_effect i zastanowienie się, ale z tą metodą musisz majsterkować. – ThatsAMorais

8

ja wpadłem na to kiedy robiłam moje własne badania. Jeśli nie dbają o przechwytywanie połączeń do methodfromdepclass(), ale wystarczy to, aby powrócić coś, po czym dodaje się może wystarczyć:

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) 

Oto parametryzowane wersja:

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

+1: ściślejsza składnia niż wzór blokowy if z 'side_effect', może powrócić Mock() zamiast żadnego tak, aby zachować domyślne zachowanie? –

+0

Dzięki za sugestię. Zostanie zaktualizowany, aby powrócić do MagicMock(). – Addison

39

Trochę słodsze :

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

lub wieloma argumentami:

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

lub o wartości domyślnej:

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

lub kombinacja obu:

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

i wesoło na wysoki idziemy.

+0

Trochę zwięźle, ale to lubię. Co się stanie, jeśli 'lambda' zwróci instancję' Mock', aby niezauważone zachowania powróciły do ​​normalnego działania biblioteki 'mock'? –