範例
我們先看一段寫得很草率的 function,這 function 用於計算除法並且會回傳商數和餘數,且他會做一些基本輸入檢查;可惜的是他目前不想支援浮點數運算
python
class UnexpectError(Exception):
def __init__(self, code, *args):
self.code = code
super().__init__(*args)
def divide(x, y):
if type(x) is float or type(y) is float:
raise UnexpectError(1001, '目前尚未支援浮點數運算')
if isinstance(x, bool) or isinstance(y, bool):
raise UnexpectError(1002, '請勿輸入布林值')
x, y = int(x), int(y)
if y == 0:
raise ZeroDivisionError('除數不可以為 0')
return x // y, x % y
接著下面是他的單元測試
python
@pytest.mark.parametrize(
('x', 'y', 'quotient', 'remainder'),
[
(4, 2, 2, 0),
(5, 3, 1, 2),
(1, 1, 1, 0),
(3, 5, 0, 3),
('4', '2', 2, 0),
('3', '5', 0, 3),
(3, '5', 0, 3),
],
)
def test_divide(x, y, quotient, remainder):
q, r = divide(x, y)
assert q == quotient, r == remainder
好像有些程式碼沒有測到
上面範例的 divide
function 單元測試毫無意外的測試過了,也就代表著 x, y = int(x), int(y)
和 return x // y, x % y
都沒問題, 不過 divide
還包含了 if
判斷的內容,這些內容在上面的單元測試就測不到了,但是故意寫符合 if
判斷的測項又會因為促發錯誤導致測試過不了; 也因為如此,目前的 coverage 只有 62%。
讓它出現錯誤吧
在這總情況下 pytest 有個好用的工具 pytest.raises
,用於預期你的測試項會出現哪寫錯誤, 例如我下面範例就預期會出現 UnexpectError
和 ZeroDivisionError
, 而我自定義的 UnexpectError
會有個屬性 code
代表錯誤代碼,我們可以在更進一步檢查是否會拿到我們預期的代碼
python
@pytest.mark.parametrize(
('x', 'y', 'error', 'code'),
[
(3, 0.3, UnexpectError, 1001),
(0.01, 2, UnexpectError, 1001),
(True, False, UnexpectError, 1002),
(12, 0, ZeroDivisionError, None)
],
)
def test_divide(x, y, error, code):
with pytest.raises(error) as e:
divide(x, y)
if code:
assert e.value.code == code