Python Mock Raise-Ausnahme

Salman Mehmood 21 Juni 2023
  1. Auslösen einer Ausnahme bei Verwendung der Unit-Testing-Bibliothek unittest in Python
  2. Manuelles Auslösen einer Ausnahme von einer Funktion
Python Mock Raise-Ausnahme

Das Hauptziel dieses Artikels ist es zu demonstrieren, wie man eine Ausnahme auslöst, wenn man die Unit-Testing-Bibliothek unittest verwendet.

Auslösen einer Ausnahme bei Verwendung der Unit-Testing-Bibliothek unittest in Python

Um zu überprüfen, ob unser Code stabil ist und die meisten Anwendungsfälle im Rahmen des Projekts berücksichtigt, müssen wir ihn testen, indem wir ihm verschiedene Eingaben geben. Eine der in Python vorhandenen Bibliotheken, unittest, bietet eine solche Funktionalität, mit der man verschiedene Testfälle schreiben und ausführen kann.

Beim Testen ist es notwendig, mit Fällen umgehen zu können, in denen die Eingabe unerwartet ist. Wir können solche Fälle behandeln, indem wir eine Ausnahme auslösen, die angibt, was mit der gegebenen Eingabe nicht stimmt.

Betrachten Sie den folgenden Code:

class A:
    def __init__(self):
        pass

    def computeData(self):
        return 0


class B:
    def method_to_test():
        obj = A()
        try:
            print(obj.computeData())
        except Exception as e:
            print("Exception at method_to_test: " + str(e))

Nehmen wir an, die Funktion, die wir testen möchten, ist method_to_test. Es initialisiert das Objekt einer anderen Klasse A und ruft dann eine seiner Methoden namens computeData in einem try-except-Block auf, um Ausnahmen abzufangen.

Wenn wir das Verhalten von computeData testen wollen, wenn eine bestimmte Ausnahme ausgelöst wird, müssen wir dies mit dem Modul unittest tun.

Ausnahme einmal auslösen

In Anbetracht des oben erwähnten Szenarios kann es auch sein, dass wir mehr Kontrolle darüber haben möchten, wann eine Ausnahme ausgelöst wird.

Es kann viele Gründe geben, warum ein solcher Bedarf entstehen kann. Abhängig von der Implementierung Ihres Codes kann es sehr hilfreich sein, mehr Kontrolle über das Verhalten zu haben.

Ausnahme bestätigen

Beim Schreiben von Testfällen möchten Sie möglicherweise prüfen, ob Ihre Ausnahmen ausgelöst werden oder nicht. Dies ist besonders nützlich, wenn Sie wissen möchten, ob eine Ausnahme ausgelöst wurde oder nicht.

Dies kann Ihnen helfen, in Ihren Testfällen früher zu erkennen, wenn etwas schief geht.

Mock-Module von Drittanbietern

Module von Drittanbietern können je nach unserer Implementierung von fast keinen bis zu vielen reichen. Um zu testen, ob sich ein Modul eines Drittanbieters wie beabsichtigt verhält, müssen wir sein Verhalten auch mit unterschiedlichen Ausgaben testen.

Ein solches Beispiel wäre request, ein weit verbreitetes Modul bei der Kommunikation über ein Netzwerk oder beim Senden/Empfangen von Daten über HTTP.

Manuelles Auslösen einer Ausnahme von einer Funktion

Um manuell eine Ausnahme von der computeData-Funktion auszulösen, müssen wir das side_effect-Attribut verwenden.

Betrachten Sie den folgenden Code:

import unittest
from unittest.mock import patch
from unittest.mock import MagicMock


class A:
    def __init__(self) -> None:
        pass

    def computeData(self):
        return 0


class B:
    def method_to_test():
        obj = A()
        try:
            print(obj.computeData())
        except Exception as e:
            print("Exception at method_to_test: " + str(e))


class Test(unittest.TestCase):
    @patch.object(A, "computeData", MagicMock(side_effect=Exception("Test")))
    def test_method(self):
        B.method_to_test()


if __name__ == "__main__":
    Test().test_method()

Ausgang:

Exception at method_to_test: Test

In der Lösung wird eine neue Methode test_method erstellt, um die Methode method_to_test zu testen. Wichtig ist auch zu beachten, dass die Funktion mit patch.object verziert wird.

Der Decorator patch wird im Modul zum Patchen von Modulen und Attributen auf Klassenebene bereitgestellt. Um genauer zu sagen, was gepatcht werden soll, verwenden wir das patch.object anstelle von patch, um die Methode direkt zu patchen.

Im Decorator wird der Klassenname A übergeben. Dies zeigt an, dass das zu patchende Objekt ein Teil von A ist, wobei der Mitgliedsname computeData übergeben wird.

Der letzte Parameter ist ein MagicMock-Objekt, eine Unterklasse von Mock, wo wir unser erforderliches Verhalten mit computeData verknüpfen, indem wir das side_effect-Attribut verwenden, das in unserem Fall eine Ausnahme auslöst.

Der allgemeine Ablauf des Programms ist wie folgt:

  1. Die test_method wird aufgerufen.
  2. Die computeData der Klasse A werden gepatcht. Ein side_effect wird zugewiesen, was in unserem Fall eine Ausnahme darstellt.
  3. Die method_to_test der Klasse B wird aufgerufen.
  4. Der Konstruktor der Klasse A wird aufgerufen, wobei die Instanz in obj gespeichert wird.
  5. Die computeData werden aufgerufen. Aufgrund der gepatchten Methode wird eine Ausnahme ausgelöst.

Lösen Sie einmal eine Ausnahme aus

Um die Ausnahme nur einmal auszulösen oder mehr Kontrolle über das Verhalten der Funktion zu haben, unterstützt side_effect mehr als nur einen Funktionsaufruf.

Aktuell unterstützt side_effect:

  1. Iterierbar
  2. Aufrufbar
  3. Ausnahme (Instanz oder Klasse)

Mit Iterable können wir dafür sorgen, dass die Methode nur einmal eine Ausnahme auslöst.

class Test(unittest.TestCase):
    @patch.object(A, "computeData", MagicMock(side_effect=[1, Exception("Test"), 3]))
    def test_method(self):
        B.method_to_test()


if __name__ == "__main__":
    testClass = Test()
    testClass.test_method()
    testClass.test_method()
    testClass.test_method()

Ausgang:

1
Exception at method_to_test: Test
3

Durch das übergebene Iterable werden Elemente unserer Wahl gepatcht und wir erhalten die Kontrolle darüber, wann Ausnahmen ausgelöst werden. Daher wird die Exception erst beim zweiten Aufruf der Funktion geworfen, andernfalls werden 1 und 3 zurückgegeben.

Ausnahme bestätigen

Um festzustellen, ob eine bestimmte Ausnahme aufgetreten ist oder nicht, können wir unittest.TestCase.assertRaises verwenden, falls unsere gewünschte Ausnahme nicht ausgelöst wird.

class Test(unittest.TestCase):
    @patch.object(A, "computeData", MagicMock(side_effect=Exception("Test")))
    def test_method(self):
        self.assertRaises(KeyError, A.computeData)


if __name__ == "__main__":
    Test().test_method()

Ausgang:

Exception at method_to_test: Test
Traceback (most recent call last):
  File "d:\Python Articles\a.py", line 28, in <module>
    Test().test_method()
  File "C:\Program Files\Python310\lib\unittest\mock.py", line 1369, in patched
    return func(*newargs, **newkeywargs)
  File "d:\Python Articles\a.py", line 25, in test_method
    self.assertRaises(KeyError, A.computeData)
  File "C:\Program Files\Python310\lib\unittest\case.py", line 738, in assertRaises
    return context.handle('assertRaises', args, kwargs)
  File "C:\Program Files\Python310\lib\unittest\case.py", line 201, in handle
    callable_obj(*args, **kwargs)
  File "C:\Program Files\Python310\lib\unittest\mock.py", line 1104, in __call__
    return self._mock_call(*args, **kwargs)
  File "C:\Program Files\Python310\lib\unittest\mock.py", line 1108, in _mock_call
    return self._execute_mock_call(*args, **kwargs)
  File "C:\Program Files\Python310\lib\unittest\mock.py", line 1163, in _execute_mock_call
    raise effect
  File "d:\Python Articles\a.py", line 17, in method_to_test
Exception: Test

Wir nehmen geringfügige Änderungen an unserer Methode test_method vor und rufen assertRaises mit zwei Parametern auf, was zu der obigen Ausgabe führte.

Die beiden Parameter können wie folgt definiert werden:

  1. KeyError - Jede Ausnahme, die Sie auslösen möchten, wird hier übergeben.
  2. A.computeData - Die Methode, die die Ausnahme auslösen soll.

Mock-Module von Drittanbietern

Um einen post zu verspotten, müssen Sie wie zuvor den Decorator patch verwenden. Ein einfaches Beispiel kann wie folgt aussehen:

class Test(unittest.TestCase):
    @patch(
        "requests.post", MagicMock(side_effect=requests.exceptions.ConnectionError())
    )
    def test_method(self):
        requests.post()

Ausgang:

Traceback (most recent call last):
  File "d:\Python Articles\a.py", line 33, in <module>
    Test().test_method()
  File "C:\Program Files\Python310\lib\unittest\mock.py", line 1369, in patched
    return func(*newargs, **newkeywargs)
  File "d:\Python Articles\a.py", line 30, in test_method
    requests.post()
  File "C:\Program Files\Python310\lib\unittest\mock.py", line 1104, in __call__
    return self._mock_call(*args, **kwargs)
  File "C:\Program Files\Python310\lib\unittest\mock.py", line 1108, in _mock_call
    return self._execute_mock_call(*args, **kwargs)
  File "C:\Program Files\Python310\lib\unittest\mock.py", line 1163, in _execute_mock_call
    raise effect
requests.exceptions.ConnectionError

Die Funktionen von Modulen von Drittanbietern können auch mit dem patch-Decorator gepatcht werden, was ein besseres Debugging und mehr Kontrolle über das Ausmaß der Zuverlässigkeit des Produkts/Programms ermöglicht.

Salman Mehmood avatar Salman Mehmood avatar

Hello! I am Salman Bin Mehmood(Baum), a software developer and I help organizations, address complex problems. My expertise lies within back-end, data science and machine learning. I am a lifelong learner, currently working on metaverse, and enrolled in a course building an AI application with python. I love solving problems and developing bug-free software for people. I write content related to python and hot Technologies.

LinkedIn

Verwandter Artikel - Python Exception

Verwandter Artikel - Python Unit Test