2016-04-03 10 views
1

Die Produktionsdatei (production_file.py) ist:python3 Mock funktioniert nicht für alle Pfade

class MyError(Exception): 
    pass 

class MyClass: 
    def __init__(self): 
     self.value = None 

    def set_value(self, value): 
     self.value = value 

    def foo(self): 
     raise RuntimeError("error!") 


class Caller: 
    def bar(self, smth): 
     obj = MyClass() 
     obj.set_value(smth) 

     try: 
      obj.foo() 
     except MyError: 
      pass 

     obj.set_value("str2") 
     obj.foo() 

Testdatei (test.py):

import unittest 

from unittest.mock import patch 
from unittest.mock import call 
from production_file import MyClass, Caller 

class MyTest(unittest.TestCase): 
    def test_caller(self): 
     with patch('production_file.MyClass', autospec=MyClass) as MyClassMock: 
      my_class_mock_obj = MyClassMock.return_value 
      my_class_mock_obj.foo.side_effect = [MyError("msg"), "text"] 

      caller = Caller() 
      caller.bar("str1") 

      calls = [call("str1"), call("str2")] 

      my_class_mock_obj.set_value.assert_has_calls(calls) 

if __name__ == '__main__': 
    unittest.main() 

Diese oben genannten Arbeiten. Aber wenn ich mich bewege die Produktionsklassen (MyError, MyClass, Anrufer) in die Testdatei und Update-Patch an:

with patch('test.MyClass', autospec=MyClass) as MyClassMock: 

dann die Instanz-Methode „foo“ nicht mehr verspottet.

Hat jemand eine Idee, warum das ist?

Ich habe auch ein ähnliches Problem mit etwas komplexeren Code, wo der Produktionscode in my_package/src/production_file.py ist, während der Test in my_package/tests/test_file.py ist. Python liefert keinen Fehler für den Pfad, der Pfad ist korrekt, aber der Mock funktioniert nicht.

Antwort

2

Wenn Sie test.py als __main__ laufen dann ist es nicht test.MyClass es __main__.MyClass wäre, oder in beiden Fällen __name__+".MyClass".

Ich konnte feststellen, dass die Klasse verwendet wird und die gepatchte Klasse unterschiedlich waren durch eine print-Anweisung hinzu:

class Caller: 
    def bar(self, smth): 
     print(MyClass) #lets see what we are actually making an instance of... 
     obj = MyClass() 
     ... 

Wenn der Patch, dass diese auf die Klasse angewendet wird, verwenden Sie so etwas wie dies sehen würde :

<MagicMock name='MyClass' spec='MyClass' id='4387629656'> 

Aber wenn in der Klasse in test.py bewegt wird man so etwas sehen:

<class '__main__.MyClass'> 

, die anzeigt: (. Zumindest die, die für den Test verwendet wird)

  1. Es gibt kein Patching MyClass angelegt war
  2. Der Name der Klasse, die __main__.MyClass
ist gepatcht werden muss

aber es ist sehr wahrscheinlich, dass Ihre „mehr ... komplizierte Situation“ wegen einer Einrichtung wie diese nicht funktioniert:

from production_file import MyClass 

class MyError(Exception): 
    pass 


class Caller: 
    def bar(self, smth): 
     print(MyClass) 
     obj = MyClass() 
     ... 

class MyTest(unittest.TestCase): 
    def test_caller(self): 
     with patch('production_file.MyClass', autospec=MyClass) as MyClassMock: 
      ... 

In diesem Fall production_file.MyClass wird gepatcht und MyClass von importiert wird production_file so die richtige Klasse gepatcht wird, aber immer noch der Ausgang ist:

<class 'production_file.MyClass'> 

Dies liegt daran, die Klasse direkt an dem lokalen Namensraum importiert wurde, so wenn der Patch auf die production_file der lokalen Namensraum noch nicht beeinflusst wird angewandt wird, können wir überprüfen, ob der Patch tatsächlich mit angewendet wurde:

... 
def bar(self, smth): 
    print(MyClass) 
    from production_file import MyClass as pf_MyClass 
    print(pf_MyClass) 
... 


#output: 
<class 'production_file.MyClass'> 
<MagicMock name='MyClass' spec='MyClass' id='4387847136'> 

Wenn dies der Fall ist, müssen Sie nur das Modul importieren, nicht die Klasse direkt .Dann, sobald der Patch angewendet wird, werden Sie es direkt aus der Datei verwenden:

+0

Interessant ... aber warum nicht Python einen Fehler ergeben, wenn der Pfad falsch ist? – Feoggou

+0

weil nichts schief läuft, wird nur an der falschen Stelle gepatcht, wenn Sie eine Datei 'test.py' haben und Sie darin' test importieren', dann haben Sie zwei Versionen derselben Datei, die mit unterschiedlichen Namen geladen sind. Wenn Sie 'if __name__ ==" __main__ "hinzugefügt haben: von Test Import MyClass ...' etc, würde es wie erwartet funktionieren, da es dann die Klasse, die Sie für den Test verwenden, patchen. –

+0

Ich habe auch ein Problem mit einer komplizierteren Situation, wo ich my_package/src/my_file.py und my_package/tests/test.py habe. In diesem Fall verwendet einer meiner Tests einen Patch ("src.my_file.MyClass"), aber der Mock funktioniert trotzdem nicht. Gibt es eine Möglichkeit für mich, zu debuggen und zu protokollieren, was der Test eigentlich tut? – Feoggou

Verwandte Themen