2015-07-09 5 views
8

Angenommen, es gibt zwei Pakete in einem Projekt: some_package und another_package.Pytest Monkeypatch funktioniert nicht bei importierter Funktion

# some_package/foo.py: 
def bar(): 
    print('hello') 

# another_package/function.py 
from some_package.foo import bar 

def call_bar(): 
    # ... code ... 
    bar() 
    # ... code ... 

Ich möchte another_package.function.call_bar testen some_package.foo.bar spöttisch, weil sie auf das Netzwerk hat I/O ich vermeiden wollen. Hier

ist ein Test:

# tests/test_bar.py 
from another_package.function import call_bar 

def test_bar(monkeypatch): 
    monkeypatch.setattr('some_package.foo.bar', lambda: print('patched')) 
    call_bar() 
    assert True 

Zu meiner Überraschung gibt sie hello statt mock. Ich habe versucht, diese Sache zu debuggen, die ipdb Haltepunkt im Test setzt. Wenn ich manuell some_package.foo.bar nach Breakpoint importieren und rufen bar() ich patched.

Bei meinem realen Projekt ist die Situation noch interessanter. Wenn ich Pytest im Projektstamm aufrufen, wird meine Funktion nicht gepatcht, aber wenn ich tests/test_bar.py als Argument festlege - funktioniert es.

Soweit ich verstehe, hat es etwas mit der from some_package.foo import bar Aussage zu tun. Wenn es ausgeführt wird, bevor Monkeypatching stattfindet, schlägt das Patchen fehl. Bei dem verdichteten Testaufbau aus dem obigen Beispiel funktioniert das Patching jedoch nicht in beiden Fällen.

Und warum funktioniert es in IPDB REPL nach dem Treffen eines Haltepunkts?

Antwort

6

genannt Einfuhr erstellt einen neuen Namen für das Objekt, wenn Sie dann den alten Namen ersetzen für das Objekt der neue Name

das Modul importieren und verwenden module.bar stattdessen nicht betroffen ist, die immer das aktuelle Objekt verwenden

edit:

import module 

def func_under_test(): 
    module.foo() 


def test_func(): 
    monkeypatch.setattr(...) 
    func_under_test 
+4

Dies ist einer der schlimmsten Fehler mit Pytest - aber danke für die Erklärung. –

+0

Wenn Sie "use module.bar" sagen, können Sie ein Codebeispiel angeben? Ich habe 'monkeypatch.setattr (Modul, 'bar', mock_obj)' und ein paar andere Beschwörungen ohne Erfolg versucht. – skolsuper

+0

Vielen Dank für Ihre Antwort. Gibt es einen Trick, um sicher zu sein, dass jeder Import ein verspottetes Objekt verwendet? Denn jetzt ist es gefährlich, wenn ich zum Beispiel versuche, Objekt – Stavinsky

1

Während Ronny's answer funktioniert es Sie zwingt Anwendungscode zu ändern. Im Allgemeinen sollten Sie dies nicht zum Testen tun.

Stattdessen können Sie das Objekt im zweiten Paket explizit patchen. Dies wird in der docs for the unittest module erwähnt.

monkeypatch.setattr('another_package.bar', lambda: print('patched')) 
Verwandte Themen