wake-up-neo.com

Wie verspotte ich eine readonly-Eigenschaft mit mock?

Wie verspotten Sie eine schreibgeschützte Eigenschaft mit mock ?

Ich habe es versucht:

setattr(obj.__class__, 'property_to_be_mocked', mock.Mock())

aber das Problem ist, dass es dann auf alle Instanzen der Klasse zutrifft ... was meine Tests bricht.

Hast du eine andere Idee? Ich möchte nicht das gesamte Objekt verspotten, sondern nur diese bestimmte Eigenschaft.

73
charlax

Ich denke, der bessere Weg ist, die Eigenschaft als PropertyMock zu verspotten, anstatt die __get__ Methode direkt.

Es steht in der Dokumentation , Suche nach unittest.mock.PropertyMock: Ein Mock, der als Eigenschaft oder anderer Deskriptor für eine Klasse verwendet werden soll. PropertyMock liefert __get__ und __set__ -Methoden, mit denen Sie einen Rückgabewert angeben können, wenn dieser abgerufen wird.

Hier ist, wie:

class MyClass:
    @property
    def last_transaction(self):
        # an expensive and complicated DB query here
        pass

def test(unittest.TestCase):
    with mock.patch('MyClass.last_transaction', new_callable=PropertyMock) as mock_last_transaction:
        mock_last_transaction.return_value = Transaction()
        myclass = MyClass()
        print myclass.last_transaction
        mock_last_transaction.assert_called_once_with()
131

Eigentlich war die Antwort (wie üblich) in der Dokumentation , es ist nur so, dass ich den Patch auf die Instanz anstatt auf die Klasse angewendet habe, als ich ihrem Beispiel gefolgt bin.

So geht's:

class MyClass:
    @property
    def last_transaction(self):
        # an expensive and complicated DB query here
        pass

In der Testsuite:

def test():
    # Make sure you patch on MyClass, not on a MyClass instance, otherwise
    # you'll get an AttributeError, because mock is using settattr and
    # last_transaction is a readonly property so there's no setter.
    with mock.patch(MyClass, 'last_transaction') as mock_last_transaction:
        mock_last_transaction.__get__ = mock.Mock(return_value=Transaction())
        myclass = MyClass()
        print myclass.last_transaction
39
charlax

Wahrscheinlich eine Frage des Stils, aber falls Sie Dekorateure in Tests bevorzugen, könnte @ jamescastlefield's answer in etwa so geändert werden:

class MyClass:
    @property
    def last_transaction(self):
        # an expensive and complicated DB query here
        pass

class Test(unittest.TestCase):
    @mock.patch('MyClass.last_transaction', new_callable=PropertyMock)
    def test(mock_last_transaction):
        mock_last_transaction.return_value = Transaction()
        myclass = MyClass()
        print myclass.last_transaction
        mock_last_transaction.assert_called_once_with()
4
Eyal Levin

Wenn das Objekt, dessen Eigenschaft Sie überschreiben möchten, ein Scheinobjekt ist, müssen Sie patch nicht verwenden.

Erstellen Sie stattdessen ein PropertyMock und überschreiben Sie dann die Eigenschaft für den Typ des Modells. Um beispielsweise die Eigenschaft mock_rows.pages Zu überschreiben und (mock_page, mock_page,) Zurückzugeben, gehen Sie wie folgt vor:

mock_page = mock.create_autospec(reader.ReadRowsPage)
# TODO: set up mock_page.
mock_pages = mock.PropertyMock(return_value=(mock_page, mock_page,))
type(mock_rows).pages = mock_pages
2
Tim Swast

Wenn Sie nicht testen möchten, ob auf die verspottete Eigenschaft zugegriffen wurde, können Sie sie einfach mit dem erwarteten return_value Patchen.

with mock.patch(MyClass, 'last_transaction', Transaction()):
    ...
0
Simon Charette