假設我有一個具有以下功能的 python 模塊:
def is_plontaria(plon: str) -> bool:
if plon is None:
raise RuntimeError("None found")
return plon.find("plontaria") != -1
對于該功能,我有以下單元測驗:
def test_is_plontaria_null(self):
with self.assertRaises(RuntimeError) as cmgr:
is_plontaria(None)
self.assertEqual(str(cmgr.exception), "None found")
給定函式中的型別提示,輸入引數應始終是已定義的字串。但是型別提示是……提示。沒有什么可以阻止用戶傳遞它想要的任何東西,當先前的操作未能回傳預期結果并且未檢查這些結果時,None 特別是一個非常常見的選項。
所以我決定在單元測驗中測驗 None 并檢查函式中的輸入不是 None 。
問題是:型別檢查器(pylance)警告我不應該在該呼叫中使用 None :
Argument of type "None" cannot be assigned to parameter "plon" of type "str" in function "is_plontaria"
Type "None" cannot be assigned to type "str"
嗯,我已經知道了,這就是測驗的目的。
消除該錯誤的最佳方法是什么?告訴 pylance 在每個測驗/檔案中忽略這種錯誤?或者假設傳遞的引數始終是正確的型別并洗掉該測驗和函式中的 None 檢查?
uj5u.com熱心網友回復:
這是一個很好的問題。我認為在您的測驗中消除該型別錯誤不是正確的方法。
不要光顧用戶
雖然我不會說這是普遍正確的做法,但在這種情況下,我絕對建議您None從is_plontaria.
想想你用這個檢查完成了什么。假設用戶呼叫is_plontaria(None),即使您使用str. 在沒有檢查的情況下,他會導致AttributeError: 'NoneType' object has no attribute 'find'一條回溯到該行return plon.find("plontaria") != -1。用戶自己認為“哎呀,該功能需要一個str”。通過您的檢查,他會導致RuntimeError理想情況下告訴他plon應該是str.
支票的目的是什么?我不會爭辯。無論哪種方式,都會引發錯誤,因為您的函式被濫用。
如果用戶float不小心通過了怎么辦?還是一個bool?或者字面上除了 a 之外的任何東西str?您想為您撰寫的每個函式的每個引數握住用戶的手嗎?
而且我不買“None是特例”的論點。當然,在代碼中“躺著”是一種常見的型別,但正如您自己指出的那樣,這仍然取決于用戶。
如果您正在使用正確型別的注釋代碼(正如您應該)并且用戶也是,那么這種情況永遠不會發生。假設用戶有另一個foo他想使用的功能,如下所示:
def foo() -> str | None:
...
s = foo()
b = is_plontaria(s)
最后一行應該導致任何值得其鹽的靜態型別檢查器引發錯誤,說is_plontaria只接受str,但提供了str和的聯合None。甚至大多數 IDE 都將該行標記為有問題。
用戶甚至在運行他的代碼之前就應該看到這一點。然后他被迫重新考慮并在呼叫您的函式之前更改foo或引入他自己的型別檢查:
s = foo()
if isinstance(s, str):
b = is_plontaria(s)
else:
# do something else
預選賽
公平地說,在某些情況下錯誤訊息非常模糊,并且不能正確地告訴呼叫者出了什么問題。在這些情況下,介紹您自己的可能會很有用。但除此之外,我總是本著 Python 的精神爭辯說,用戶應該被認為足夠成熟,可以自己做作業。如果他不這樣做,那是他的事,而不是你。(只要你做了功課。)
可能還有其他情況,提出您自己的型別錯誤是有意義的,但我認為那些是例外。
如果必須,請使用Mock
作為一個小獎勵,如果您確實想要保留該檢查并需要if在測驗中覆寫該 -branch,您可以簡單地將 aMock作為引數傳遞,前提是您的if-statement 已調整為檢查除以下內容之外的任何內容str:
from unittest import TestCase
from unittest.mock import Mock
def is_plontaria(plon: str) -> bool:
if not isinstance(plon, str):
raise RuntimeError("None found")
return plon.find("plontaria") != -1
class Test(TestCase):
def test_is_plontaria(self) -> None:
not_a_string = Mock()
with self.assertRaises(RuntimeError):
is_plontaria(not_a_string)
...
大多數型別檢查器認為Mock是一種特殊情況,并且不會抱怨它的型別,假設您正在運行測驗。mypy例如,對這樣的代碼非常滿意。
這在其他情況下也很方便。例如,當正在測驗的函式需要您的某個自定義類的實體作為其引數時。您顯然希望將該函式與該類隔離,因此您可以通過這種方式將模擬傳遞給它。型別檢查器不會介意。
希望這可以幫助。
uj5u.com熱心網友回復:
您可以在帶有注釋的特定行上禁用型別檢查。
def test_is_plontaria_null(self):
with self.assertRaises(RuntimeError) as cmgr:
is_plontaria(None) # type: ignore
self.assertEqual(str(cmgr.exception), "None found")
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/515017.html
標籤:Pythonpython-3.x单元测试python-unittest火药
