測試你的套件

開發 Python 套件

James Fulton

Climate informatics researcher

測試的技藝與規範

想像你正在撰寫這個函式:

def get_ends(x):
    """Get the first and last element in a list"""
    return x[0], x[-1]

你可能會先測試一下是否正常運作

# 檢查函式
get_ends([1,1,5,39,0])
(1, 0)
開發 Python 套件

測試的技藝與規範

好的套件會自豪於他們有多少測試。

  • pandas 套件有 91% 的程式碼被測試覆蓋! Pandas coverage badge says 91% of functions are tested
開發 Python 套件

撰寫測試

def get_ends(x):
    """Get the first and last element in a list"""
    return x[0], x[-1]
def test_get_ends():
    assert get_ends([1,5,39,0]) == (1,0)
test_get_ends()


開發 Python 套件

撰寫測試

def get_ends(x):
    """Get the first and last element in a list"""
    return x[0], x[1]
def test_get_ends():
    assert get_ends([1,5,39,0]) == (1,0)
test_get_ends()
AssertionError: 
...
開發 Python 套件

撰寫測試

def get_ends(x):
    """Get the first and last element in a list"""
    return x[0], x[-1]
def test_get_ends():
    assert get_ends([1,5,39,0]) == (1,0)
    assert get_ends(['n','e','r','d']) == ('n','d')
開發 Python 套件

在套件內組織測試

mysklearn/
|-- mysklearn   <-- 套件
|-- tests       <-- 測試目錄
|-- setup.py
|-- LICENSE
|-- MANIFEST.in
開發 Python 套件

在套件內組織測試

測試目錄結構

mysklearn/tests/
|-- __init__.py
|-- preprocessing

| |-- __init__.py | |-- test_normalize.py
| |-- test_standardize.py |-- regression | |-- __init__.py | |-- test_regression.py |-- test_utils.py

原始碼目錄結構

mysklearn/mysklearn/
|-- __init__.py
|-- preprocessing

| |-- __init__.py | |-- normalize.py
| |-- standardize.py |-- regression | |-- __init__.py | |-- regression.py |-- utils.py
開發 Python 套件

組織一個測試模組

test_normalize.py

from mysklearn.preprocessing.normalize import (
    find_max, find_min, normalize_data
)

def test_find_max(x):
    assert find_max([1,4,7,1])==7

def test_find_min(x):
    assert ...

def test_normalize_data(x):
    assert ...

DataCamp:資料科學的單元測試

normalize.py

def find_max(x):
    ...
    return x_max

def find_min(x):
    ...
    return x_min

def normalize_data(x):
    ...
    return x_norm
開發 Python 套件

用 pytest 執行測試

pytest
  • pytest 會在 test 目錄內尋找
  • 會尋找像 test_modulename.py 的模組
  • 會尋找像 test_functionname() 的函式
  • 它會執行這些函式並顯示輸出
mysklearn/ <-- 導覽到這裡
|-- mysklearn
|-- tests
|-- setup.py
|-- LICENSE
|-- MANIFEST.in
開發 Python 套件

用 pytest 執行測試

pytest
======================== test session starts ========================
platform linux -- Python 3.7.9, pytest-6.1.2, py-1.9.0, pluggy-0.13.1
rootdir: /home/workspace/mypackages/mysklearn
collected 6 items

tests/preprocessing/test_normalize.py ...                     [ 50%]
tests/preprocessing/test_standardize.py ...                   [100%]

========================= 6 passed in 0.23s =========================
開發 Python 套件

用 pytest 執行測試

pytest
======================== test session starts ========================
platform linux -- Python 3.7.9, pytest-6.1.2, py-1.9.0, pluggy-0.13.1
rootdir: /home/workspace/mypackages/mysklearn     <--    在此目錄執行
collected 6 items                                 <--    找到 6 個測試函式

tests/preprocessing/test_normalize.py ...                     [ 50%]
tests/preprocessing/test_standardize.py ...                   [100%]

========================= 6 passed in 0.23s =========================
開發 Python 套件

用 pytest 執行測試

pytest
======================== test session starts ========================
platform linux -- Python 3.7.9, pytest-6.1.2, py-1.9.0, pluggy-0.13.1
rootdir: /home/workspace/mypackages/mysklearn
collected 6 items

tests/preprocessing/test_normalize.py ...                     [ 50%]  <-- 
tests/preprocessing/test_standardize.py ...                   [100%]  <-- 

========================= 6 passed in 0.23s =========================
開發 Python 套件

用 pytest 執行測試

pytest
======================== test session starts ========================
platform linux -- Python 3.7.9, pytest-6.1.2, py-1.9.0, pluggy-0.13.1
rootdir: /home/workspace/mypackages/mysklearn
collected 6 items

tests/preprocessing/test_normalize.py ...                     [ 50%]
tests/preprocessing/test_standardize.py ...                   [100%]

========================= 6 passed in 0.23s =========================
開發 Python 套件

用 pytest 執行測試

pytest
==================================== test session starts ====================================
...
tests/preprocessing/test_normalize.py .F.                                              [ 50%]
tests/preprocessing/test_standardize.py ...                                            [100%]

========================================= FAILURES ==========================================
________________________________________ test_mymax _________________________________________
...

tests/preprocessing/test_normalize.py:10: AssertionError
================================== short test summary info ==================================
FAILED tests/preprocessing/test_normalize.py::test_mymax - assert -100 == 100       <-- test_mymax
================================ 1 failed, 5 passed in 0.17s ================================
開發 Python 套件

一起來練習吧!

開發 Python 套件

Preparing Video For Download...