Python assert 異常

Neema Muganga 2023年10月10日
  1. 使用上下文管理器捕獲 Python assert 異常
  2. 使用關鍵字引數捕獲 Python assert 異常
Python assert 異常

這篇文章讓人們瞭解 assert 作為一個測試單元,以測試函式是否可以丟擲異常(程式碼執行期間檢測到的錯誤)而不必退出執行。換句話說,丟擲的異常被封裝了。

如果引發異常,則此測試將通過。如果丟擲不同於預期的異常,則會丟擲錯誤。在根本沒有引發異常的例項中,測試失敗。

使用上下文管理器捕獲 Python assert 異常

與通用 Python 概念中必要時允許分配和釋放資源的方式類似,這裡的上下文獲取測試期間丟擲的實際異常物件。

如果需要對引發的異常進行額外的效能檢查,它會將這個異常屬性儲存在物件中。

為了測試函式是否失敗或通過是否丟擲異常,我們將使用 unittest 模組中的 TestCase.assertRaises

讓我們看一個使用上下文管理器的實際示例。

import unittest


class TestCase(unittest.TestCase):
    def test_nameerror(self):
        with self.assertRaises(Exception):
            100 * (someNumber / 5)


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

輸出:

.
----------------------------------------------------------------------
Ran 1 test in 0.002s

OK

在示例程式碼中,測試通過是因為我們期望丟擲異常。我們將 5 除以未定義的變數 someNumber。因此,該函式會引發 NameError 異常。因此,我們的測試通過了,你可以看到 . 顯示在輸出的第一行。

讓我們看一個例子,當沒有丟擲異常時測試失敗。

import unittest


class TestCase(unittest.TestCase):
    def test_nameerror(self):
        with self.assertRaises(Exception):
            someNumber = 10
            100 * (someNumber / 5)


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

輸出:

F
======================================================================
FAIL: test_nameerror (__main__.MyTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):  File "c:\Users\Neema\Desktop\Article Requirement Spec and Example 
Articles\Article Requirement Spec 
and Example Articles\fah.py", line 106, in test_nameerror
    100 * (someNumber/5)
AssertionError: Exception not raised

----------------------------------------------------------------------
Ran 1 test in 0.002s

FAILED (failures=1)

在此示例程式碼中,我們為 someNumber 定義了一個值,然後用它執行算術表示式。

輸出向我們顯示測試按預期失敗。我們不希望這次丟擲任何 NameError 異常,因為 someNumber 的定義值確實存在。

我們也可以使用使用者定義的異常來實現上下文管理器,如下例所示。

import unittest


def user_function():
    raise Exception("A user-defined exception")


class MyTestCase(unittest.TestCase):
    def test_userexception(self):
        with self.assertRaises(Exception) as context:
            user_function()

        self.assertTrue("A user-defined exception" in str(context.exception))


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

輸出:

.
----------------------------------------------------------------------
Ran 1 test in 0.006s

OK

請記住使用 strcontext.exception 括起來,以避免出現 TypeError。

我們引入了 assertTrue,一個 unittest 庫,用於在單元測試中將測試值與 true 進行比較。

你還可以選擇使用 assertIn 而不是 assertTrue

例子:

import unittest


def user_function():
    raise Exception("A use defined exception")


class MyTestCase(unittest.TestCase):
    def test_userexception(self):
        with self.assertRaises(Exception) as context:
            user_function()

        self.assertIn("A use defined exception", str(context.exception))


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

我們看到將產生測試通過的相同輸出。

使用關鍵字引數捕獲 Python assert 異常

與上下文管理器不同,我們只將異常傳遞給 assertRaises(),我們還將函式呼叫和函式引數作為關鍵字引數傳遞來引發異常。

語法

assertRaises(exception, function, *args, **keywords)

例子:

import unittest


class MyTestCase(unittest.TestCase):
    def test_division_by_error(self):
        import operator

        self.assertRaises(ZeroDivisionError, operator.floordiv, 55, 0)


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

輸出:

.
----------------------------------------------------------------------
Ran 1 test in 0.002s

OK

上面的示例程式碼使用帶有關鍵字引數的 assertRaises()。在嘗試將數字除以零後,我們將預期的 ZeroDivisionError 異常傳遞給它。我們匯入了與 floordiv 運算子函式一起使用的運算子函式作為第二個引數。這裡的引數值是第三個引數:55 除以 0。

assert 的最後一個應用是當我們向 assertRaises() 傳入一個不同於預期的異常時。反而會產生錯誤。

使用我們剛剛在上面使用的相同示例程式碼,讓我們實現它。

import unittest


class MyTestCase(unittest.TestCase):
    def test_division_by_error(self):
        import operator

        self.assertRaises(TypeError, operator.floordiv, 55, 0)


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

輸出:

E
======================================================================
ERROR: test_division_by_error (__main__.MyTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):  File "c:\Users\Neema\Desktop\Article Requirement Spec and Example 
Articles\user.py", line 16, in test_division_by_error
    self.assertRaises(TypeError, operator.floordiv, 55, 0)
  File "C:\Users\Neema\AppData\Local\Programs\Python\Python39\lib\unittest\case.py", line 733, in assertRaises
    return context.handle('assertRaises', args, kwargs)
  File "C:\Users\Neema\AppData\Local\Programs\Python\Python39\lib\unittest\case.py", line 201, in handle
    callable_obj(*args, **kwargs) 
ZeroDivisionError: integer division or modulo by zero

----------------------------------------------------------------------
Ran 1 test in 0.031s

FAILED (errors=1)

從輸出中,會產生一個錯誤回溯。當我們將一個數字除以零時,我們預計會出現 ZeroDivisionError 異常。相反,我們傳入一個 TypeError 異常,在不同資料型別之間執行算術運算時應用,比如字串和整數。

相關文章 - Python Exception