TLDR:上游函式被呼叫時引數順序錯誤。我怎樣才能確保測驗能發現這個問題呢?
下面是我的一個最小的設定示例:
# functions.py
def inner(age, name) 。
if age > 18:
return f'{name}是一個成年人。
else:
return f'{name}是一個孩子。
def outer(name, age)。
info = inner(name, age)
return f'This is {name}. {info}'。
# tests.py。
from functions import inner, outer
from unittest.mock import Mock, patch
def test_inner()。
name, age = "John", 43
資訊 = inner(age, name)
expected = "John是一個成年人"。
assert info == expected
def test_outer()。
name, age = "John", 43.
mock_get_info = Mock()
mock_get_info.return_value = "info"
patch_get_info = patch("function.inner", new=mock_get_info)
with patch_get_info:
info = outer(name, age)
expected = 'This is John. 資訊'
assert info == expected
mock_get_info.assert_called_once_with(name, age)
功能:
- 兩個函式及其相應的測驗。
inner函式產生一個字串,由test_inner函式檢查是否正確。outer函式呼叫inner函式并將其連接到它自己的字串。正確的連接和inner函式的呼叫是由test_outer函式檢查的。- 另外,除了這個最小的例子,
inner函式可能會產生一個非常大的字串,我不想在test_outer中明確檢查,這就是為什么inner的回傳值是模擬的。
你可能已經注意到,outer函式實際上以錯誤的方式將引數傳遞給inner函式。這是因為我可以決定改變inner函式的引數順序,并相應地改變test_inner,但忘記了outer函式呼叫inner函式。這并沒有被test_outer捕捉到,因為它是內部一致的。我們在生產中才發現,inner函式拋出了一個錯誤。
我如何確保所有下游函式的測驗都能捕捉到修改后的函式定義?
uj5u.com熱心網友回復: 我想你的思路是正確的。我相信你的測驗沒有抓住它,因為你斷言的順序是錯誤的。
盡管如此,我認為一個更穩健的方法是使用關鍵字引數,然后斷言呼叫引數是預期的字典。
例如: 如果你想更嚴格,你可以通過使用 示例: 如果有人在沒有使用關鍵字引數的情況下呼叫它,Python 將在運行時引發一個例外。
事實上,像這樣的錯誤 [1] https://www.python.org/dev/peps/pep-3102/ uj5u.com熱心網友回復: 你可能需要一些集成測驗,或者功能/端到端測驗來捕捉這種錯誤。測驗單一的單元,將所有的東西放在外面是很好的,因為你的單元測驗是獨立于其他單元的錯誤。然而,正如你所發現的,你可能會在函式介面上出現問題,因為你沒有測驗它們是否被正確使用(也就是說,你可以說你沒有測驗單元集成)。因此,你可以在這個地方引入某種集成測驗(或端到端,取決于測驗策略,你可以在搜索測驗金字塔時閱讀更多相關內容)。
你的案例的測驗實體: 那么你也可以考慮是否要一直運行這樣的測驗,或者例如每晚運行一次,而在構建程序中只運行單元測驗( uj5u.com熱心網友回復: 修補是指將測驗與該物件的實際實作解耦。也就是說,用一個模擬來替換內部,可以確保內部的任何變化都不會影響該測驗。這意味著每當inner被改變時,mock必須被手動更新。鑒于最初的問題涉及到沒有識別出需要更新的 outer,要求單元測驗中的模擬對該未識別的方法進行更新是不合理的。
快速的解決方案。型別提示 嚴格的解決方案。內層定義中的斷言和下層的補丁mock_get_info.assert_called_once_with(name, age)應該是。
mock_get_info.assert_called_once_with(age, name)以匹配inner(age, name)簽名。
# functions.py。
def outer(name, age)。
info = inner(age=年齡, name=姓名)
return f'This is {name}. {info}'。
# tests。
def test_outer()。
name, age = "John", 43.
mock_get_info = Mock()
mock_get_info.return_value = "Info""function.inner", new=mock_get_info)
with patch_get_info:
info = outer(name, age)
expected = 'This is John. 資訊'
assert info == expected
_, actual_kwargs = mock_get_info.call_args
assert actual_kwargs == {'name': name, 'age': age}。
*來強制要求inner只接受關鍵字引數[1]。但這也意味著你必須提供默認值,這取決于你的使用情況,可能沒有意義。
def inner(*, age=0, name='default')。)
if age > 18:
return f'{name}是一個成年人。
else:
return f'{name}是一個孩子。
Traceback (most recent call last):
檔案 "/home/user/projects/so-test/functions.py", line 14, in <module>
outer('user', 32)
檔案 "/home/user/projects/so-test/functions.py", line 9, in outer
info = inner(age, name)
TypeError: inner()需要0個位置引數,但是2個引數被給出。
inner(age=name, name=age)要比這個inner(age, name)難得多。import pytest
@pytest.mark.functional
@pytest.mark.functional。 parametrize("name,age,expected", [("John", 18, "this is John。), ("Dave", 17, "this is Dave. 戴夫是個孩子")]))
def test_outer_functionality(name, age, expected)。
assert outer(name, age) == expected
pytest -v -m "not functional")。
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/310313.html
標籤:
下一篇:寫入csv檔案時雙引號有時會消失
