函數(Functions)
pytest.approx
斷言兩個數字(或兩組數字)在某個容差范圍內彼此相等。
由於浮點運算的復雜性,我們直覺期望相等的數字並不總是如此:
0.1 + 0.2 == 0.3
False
編寫測試時通常會遇到此問題,例如,確保浮點值是您期望的值。處理此問題的一種方法是斷言兩個浮點數等於某個適當的容差范圍內:
abs((0.1 + 0.2) - 0.3) < 1e-6
True
但是,這樣的比較寫作起來既乏味又難以理解。此外,通常不鼓勵像上面這樣的絕對比較,因為沒有適合所有情況的容忍度。1e-6
對周圍的數字有好處1
,但對於非常大的數字而言太小而對於非常小的數字而言太大。最好將公差表示為預期值的一小部分,但這樣的相對比較更難以正確和簡潔地書寫。
該approx
班采用語法是盡可能直觀執行浮點比較:
from pytest import approx
>>> 0.1 + 0.2 == approx(0.3)
True
相同的語法也適用於數字序列:
(0.1 + 0.2,0.2 + 0.4) == approx((0.3,0.6))
True
字典值:
{'a': 0.1 + 0.2,'b': 0.2 + 0.4} == approx({'a': 0.3,'b': 0.6})
True
numpy
數組:
import numpy as np
>>> np.array([0.1,0.2]) + np.array([0.2,0.4]) == approx(np.array([0.3,0.6]))
True
對於numpy
標量的數組:
import numpy as np
>>> np.array([0.1,0.2]) + np.array([0.2,0.1]) == approx(0.3)
True
默認情況下,approx
將1e-6
其預期值的相對容差(即百萬分之一)內的數字視為相等。如果預期值是這樣的0.0
,那么這種處理會產生令人驚訝的結果,因為除了0.0
本身之外什么都不是0.0
。為了不那么令人驚訝地處理這種情況,approx
還要考慮在1e-12
其預期值的絕對容差內的數字是相等的。Infinity和NaN是特殊情況。無論相對容差如何,無窮大只被視為與自身相等。默認情況下,NaN不被視為等於任何內容,但您可以通過將nan_ok
參數設置為True來使其等於自身。(這是為了便於比較使用NaN的數組表示“無數據”。)
通過將參數傳遞給approx
構造函數,可以更改相對容差和絕對容差:
1.0001 == approx(1)
False
>>> 1.0001 == approx(1,rel=1e-3)
True
>>> 1.0001 == approx(1,abs=1e-3)
True
如果您指定abs
但不指定rel
,則比較將不會考慮相對容差。換句話說,1e-6
如果超過指定的絕對容差,則默認相對容差范圍內的兩個數字仍將被視為不相等。如果同時指定兩個abs
和rel
,如果滿足任一公差,則數字將被視為相等:
1 + 1e-8 == approx(1)
True
>>> 1 + 1e-8 == approx(1,abs=1e-12)
False
>>> 1 + 1e-8 == approx(1,rel=1e-6,abs=1e-12)
True
如果您正在考慮使用approx
,那么您可能想知道它與比較浮點數的其他好方法的比較。所有這些算法都基於相對和絕對容差,並且應該在大多數情況下達成一致,但它們確實存在有意義的差異:
math.isclose(a,b,rel_tol=1e-9,abs_tol=0.0)
:True如果相對誤差滿足WRT無論是a
或b
,或者如果絕對容差得到滿足。因為相對容差是a
和兩者一起計算的b
,所以該測試是對稱的(即既不是a
也不b
是“參考值”)。如果要進行比較,則必須指定絕對容差,0.0
因為默認情況下沒有容差。僅在python> = 3.5中可用。[更多信息…numpy.isclose(a,b,rtol=1e-5,atol=1e-8)
:如果和之間的差值小於相對容差wrt和絕對容差之a
和,b
則為真b
。由於相對容差僅計算為wrtb
,因此該測試是不對稱的,您可以將其b
視為參考值。支持比較序列由numpy.allclose
。提供。更多信息…unittest.TestCase.assertAlmostEqual(a,b)
:如果a
並且b
在絕對容差范圍內,則為真1e-7
。不考慮相對容差,並且不能改變絕對容差,因此該功能不適用於非常大或非常小的數字。此外,它只在子類中可用,unittest.TestCase
並且它很難看,因為它不遵循PEP8。[更多信息…a==pytest.approx(b,rel=1e-6,abs=1e-12)
:如果滿足相對容差b
或者滿足絕對容差,則為真。由於相對容差僅計算為wrtb
,因此該測試是不對稱的,您可以將其b
視為參考值。在明確指定絕對公差而非相對公差的特殊情況下,僅考慮絕對公差。
警告:在3.2版中更改。
為了避免不一致的行為,TypeError
是提高了>
,>=
,<
和<=
比較。以下示例說明了問題:
assert 0.1 + 1e-10 # calls approx(0.1).__gt__(0.1 + 1e-10)
assert 0.1 + 1e-10 > approx(0.1) # calls approx(0.1).__lt__(0.1 + 1e-10)
在第二個例子中,人們期望被調用。但相反,用於比較。這是因為豐富比較的調用層次結構遵循固定行為。
pytest.fail
參考:Skip和xfail:處理無法成功的測試
fail(msg='', pytrace=True): 使用給定消息顯式地設置用例為失敗狀態。
參數:
- msg(str) - 顯示用戶失敗原因的消息。
- pytrace(bool) - 如果為false,則msg表示完整的失敗信息,並且不報告任何python回溯。
pytest.skip
skip(msg[, allow_module_level=False]): 使用給定消息跳過測試用例。
應僅在測試(設置,調用或拆除)期間或使用allow_module_level
標志在收集期間調用此函數。此函數也可以在doctests中調用。
參數:
- allow_module_level(bool) - 允許在模塊級別調用此函數,跳過模塊的其余部分。默認為False。
注意:最好在可能的情況下使用
pytest.mark.skipif
標記來聲明在某些條件下跳過的測試,例如不匹配的平台或依賴項。 同樣,使用#doctest:+ SKIP
指令(請參閱doctest.SKIP)可以靜態地跳過doctest。
pytest.importorskip
importorskip(modname, minversion=None, reason=None): 導入並返回請求的模塊modname
,或者如果無法導入模塊,則跳過當前測試。
參數:
- modname(str) - 要導入的模塊的名稱
- minversion(str) - 如果給定,導入的模塊
__version__
屬性必須至少為此最小版本,否則仍會跳過測試。 - reason(str) - 如果給定,則無法導入模塊時,此原因顯示為消息。
pytest.xfail
xfail(reason=''): 由於給定的原因,強制標記失敗測試用例或測試准備函數。
只能在測試函數, setup函數或teardown函數中使用此函數。
注意:最好在可能的情況下使用
pytest.mark.xfail
標記,在某些條件(如已知錯誤或缺少的功能)下聲明測試是否為xfailed。
pytest.exit
exit(msg, returncode=None): 退出測試過程。
參數:
- msg(str) - 退出時顯示的消息。
- returncode(int) - 返回退出pytest時使用的代碼。
pytest.main
main(args=None, plugins=None): 執行進程內測試運行后返回退出代碼。
參數:
- args- 命令行參數列表。
- plugins- 初始化期間要自動注冊的插件對象列表。
pytest.param
param(*values[, id][, marks]): 在[pytest.mark.parametrize指定參數。
@pytest.mark.parametrize("test_input,expected",[
("3+5",8),
pytest.param("6*9",42,marks=pytest.mark.xfail),
])
def test_eval(test_input,expected):
assert eval(test_input) == expected
參數:
- values- 按順序的參數集值的變量args。
- 標記- 要應用於此參數集的單個標記或標記列表。
- id(str) - 屬於此參數集的id。
pytest.raises
參考:關於預期異常的斷言。
with raises(expected_exception: Exception[, match][, message]) as excinfo:
斷言代碼塊/函數調用會引發expected_exception
或引發失敗異常。
參數:
- match- 如果指定,則斷言異常與text或regex匹配
- 消息-(自4.1棄用)如果指定,提供了一種定制的失敗消息,如果異常沒有升高
使用pytest.raises
的上下文管理器,這將捕獲特定類型的異常:
>>> with raises(ZeroDivisionError):
... 1/0
如果代碼塊沒有引發預期的異常(ZeroDivisionError
在上面的示例中),或者根本沒有異常,則檢查將失敗。
你還可以使用keyword參數match
來斷言異常與text或regex匹配:
>>> with raises(ValueError,match='must be 0 or None'):
... raise ValueError("value must be 0 or None")
>>> with raises(ValueError,match=r'must be \d+/pre>):
... raise ValueError("value must be 42")
上下文管理器生成一個ExceptionInfo
對象,可用於檢查捕獲的異常的詳細信息:
>>> with raises(ValueError) as exc_info:
... raise ValueError("value must be 42")
>>> assert exc_info.type is ValueError
>>> assert exc_info.value.args[0] == "value must be 42"
自4.1版本后不推薦使用:在上下文管理器表單中,您可以使用keyword參數message
指定在pytest.raises
檢查失敗時將顯示的自定義失敗消息。這已被棄用,因為它被認為是容易出錯的,因為用戶通常意味着使用它match
。
注意:當
pytest.raises
用作上下文管理器時,值得注意的是,應用正常的上下文管理器規則,並且引發的異常必須是上下文管理器范圍內的最后一行。之后,在上下文管理器范圍內的代碼行將不會被執行。例如:
value = 15
>>> with raises(ValueError) as exc_info:
... if value > 10:
... raise ValueError("value must be <= 10")
... assert exc_info.type is ValueError # this will not execute
相反,必須采取以下方法(注意范圍的差異):
with raises(ValueError) as exc_info:
... if value > 10:
... raise ValueError("value must be <= 10")
...
>>> assert exc_info.type is ValueError
使用pytest.mark.parametrize
使用pytest.mark.parametrize時,可以對測試進行參數化,使得某些運行引發異常,而其他運行則不會。
有關示例,請參閱[參數化條件提升。
遺產形式
可以通過傳遞一個名為lambda來指定一個可調用的:
raises(ZeroDivisionError,lambda: 1/0)
<ExceptionInfo ...>
或者你可以用參數指定一個任意的callable:
def f(x): return 1/x
...
>>> raises(ZeroDivisionError,f,0)
<ExceptionInfo ...>
>>> raises(ZeroDivisionError,f,x=0)
<ExceptionInfo ...>
上面的表單完全受支持,但不建議使用新代碼,因為上下文管理器表單被認為更具可讀性且不易出錯。
注意:與Python中捕獲的異常對象類似,顯式清除對返回
ExceptionInfo
對象的本地引用可以幫助Python解釋器加速其垃圾回收。
清除這些引用會中斷引用循環(ExceptionInfo
- >捕獲異常 - >幀堆棧引發異常 - >當前幀堆棧 - >局部變量 - >ExceptionInfo
),這會使Python保留從該循環引用的所有對象(包括當前幀中的所有局部變量) )活着,直到下一個循環垃圾收集運行。有關try
更多詳細信息,請參閱官方Python語句文檔。
pytest.deprecated_call
參考:確保代碼觸發棄用警告。
with deprecated_call(): 上下文管理器,可用於確保代碼塊觸發aDeprecationWarning
或PendingDeprecationWarning
:
import warnings
>>> def api_call_v2():
... warnings.warn('use v3 of this api',DeprecationWarning)
... return 200
>>> with deprecated_call():
... assert api_call_v2() == 200
deprecated_call
也可以通過傳遞函數來使用,*args
並且*kwargs
在這種情況下,它將確保調用產生上面的警告類型之一。func(*args,**kwargs)
pytest.register_assert_rewrite
參考: 斷言重寫。
register_assert_rewrite(*names): 注冊一個或多個要在導入時重寫的模塊名稱。
此函數將確保此模塊或程序包內的所有模塊將重寫其assert語句。因此,您應確保在實際導入模塊之前調用此方法,如果您是使用包的插件,則通常在__init__.py中調用。
拋出:TypeError- 如果給定的模塊名稱不是字符串。
pytest.warns
參考: 使用警告功能發出警告
with warns(expected_warning: Exception[, match]): 斷言代碼會引發一類特定的警告。
具體來說,參數expected_warning
可以是警告類或警告類序列,並且with
塊內部必須發出該類或類的警告。
該助手生成一個warnings.WarningMessage
對象列表,每個警告引發一個對象。
此函數可用作上下文管理器,pytest.raises
也可以使用任何其他方法:
with warns(RuntimeWarning):
... warnings.warn("my warning",RuntimeWarning)
在上下文管理器表單中,您可以使用keyword參數match
來斷言異常與text或regex匹配:
with warns(UserWarning,match='must be 0 or None'):
... warnings.warn("value must be 0 or None",UserWarning)
>>> with warns(UserWarning,match=r'must be \d+/pre>):
... warnings.warn("value must be 42",UserWarning)
>>> with warns(UserWarning,match=r'must be \d+/pre>):
... warnings.warn("this is not here",UserWarning)
Traceback (most recent call last):
...
Failed: DID NOT WARN. No warnings of type ...UserWarning... was emitted...
pytest.freeze_includes
參考: 凍結pytest。
freeze_includes(): 返回pytest使用的模塊名稱列表,應由cx_freeze包含。