Skip to content

好用的 pytest 之 parametrize

📅 6/10/2024

上一篇文章中,我們利用 leetcode 的題目介紹如何用 pytest 寫單元測試, 不過我們只用了一組測試資料,感覺對測試對象還不是那麼有信心,所以我們來多增加一些測項吧!

更多測項

python
def test_solution():
    nums = [4, 5, 0, -2, -3, 1]
    k = 5
    ans = 7

    solution = Solution()
    assert ans == solution.subarray_div_by_k(nums=nums, k=k)

    nums = [5]
    k = 9
    ans = 0
    assert ans == solution.subarray_div_by_k(nums=nums, k=k)

    nums = [23, 2, 6, 4, 7]
    k = 6
    ans = 3
    assert ans == solution.subarray_div_by_k(nums=nums, k=k)

    nums = [-5]
    k = 5
    ans = 1
    assert ans == solution.subarray_div_by_k(nums=nums, k=k)

    nums = [0]
    k = 2
    ans = 1
    assert ans == solution.subarray_div_by_k(nums=nums, k=k)

我們補上那麼多測項而測試依然可以通過,感覺信心值也拉滿了

唯一的問題就是這單元測試好像太冗長了,無法一眼就看出你要測試什麼 , 如果別人想透過單元測試了解你寫的程式碼,他反而要先看你這一長串測試代碼, 有人或許已經想到要用迴圈或 mapping 去管理測資,不過既然我們都要用 pytest 框架了,測資這種小事就也交給他吧

使用 parametrize

python
@pytest.mark.parametrize(
    ('nums', 'k', 'ans'),
    [
        ([4, 5, 0, -2, -3, 1], 5, 7),
        ([5], 9, 0),
        ([-5], 5, 1),
        ([0], 2, 1)
    ]
)
def test_solution(ans, nums, k):
    solution = Solution()
    assert ans == solution.subarray_div_by_k(nums=nums, k=k)

程式碼說明:

  • 第一個參數(argnames) 要填入我們測資的參數名稱,而該參數名稱也要在測試函數有個接收的參數,只要名字有對上就可以
  • 第二個參數(argvalues) 則是我們測資的值,任何型態都可以
  • 測試函數一樣透過斷言來判斷是不是我們預期的答案

透過裝飾器 pytest.mark.parametrize 就可以方便的管理多個測資,整個測試看起來也清爽很多

接著在 terminal 輸入 pytest,可以看到 pytest 是把它當成多個測試項

如果輸入 pytest -v 有更詳細的測項資訊

ids

可以再透過 ids 給每個測項一個專屬名稱

python
@pytest.mark.parametrize(
    ('nums', 'k', 'ans'),
    [
        ([4, 5, 0, -2, -3, 1], 5, 7),
        ([5], 9, 0),
        ([-5], 5, 1),
        ([0], 2, 1)
    ],
    ids=['case0', 'case1', 'case2', 'case3']
)
def test_solution(ans, nums, k, request):
    # print(request.node.callspec.id)  # 如果你想在程式碼中看到 id 可以加這句
    solution = Solution()
    assert ans == solution.subarray_div_by_k(nums=nums, k=k)

只是不會像動漫一樣能讓命名魔物變強;命名的測項只是方便你看而已,不會進化

哈哈