Mục lục- Phương thức Fixture và tham số hóa test case
- @pytest.mark.parametrize: Tham số hóa hàm test
- Ví dụ cơ bản về pytest_generate_tests
- Các ví dụ mở rộng
Quay lại: Hướng dẫn Pytest chuyên sâu
Phương thức Fixture và tham số hóa test case
Pytest hỗ trợ tham số hóa test ở nhiều cấp độ:
pytest.fixture()cho phép tạo phương thức Fixture tham số hóa.@pytest.mark.parametrizecho phép định nghĩa nhiều bộ tham số và Fixture trong test function hoặc class.pytest_generate_testscho phép người dùng tự định nghĩa lược đồ tham số hóa hoặc mở rộng.
@pytest.mark.parametrize: Tham số hóa hàm test
Tính năng mới từ phiên bản 2.2.
Cải tiến trong phiên bản 2.4.
Decorator pytest.mark.parametrize tích hợp sẵn hỗ trợ tham số hóa test function. Dưới đây là ví dụ kiểm tra đầu vào có cho ra đầu ra kỳ vọng:
# nội dung của test_expectation.py
import pytest
@pytest.mark.parametrize("test_input,expected",[("3+5",8),("2+4",6),("6*9",42)])
def test_eval(test_input,expected):
assert eval(test_input) == expected
Trong ví dụ này, decorator @parametrize định nghĩa 3 bộ tham số (test_input,expected) dưới dạng tuple, khiến hàm test_eval chạy 3 lần:
$ pytest
=========================== test session starts ============================
platform linux -- Python 3.x.y,pytest-4.x.y,py-1.x.y,pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 3 items
test_expectation.py ..F [100%]
================================= FAILURES =================================
____________________________ test_eval[6*9-42] _____________________________
test_input = '6*9',expected = 42
@pytest.mark.parametrize("test_input,expected",[("3+5",8),("2+4",6),("6*9",42)])
def test_eval(test_input,expected):
> assert eval(test_input) == expected
E AssertionError: assert 54 == 42
E + where 54 = eval('6*9')
test_expectation.py:6: AssertionError
==================== 1 failed,2 passed in 0.12 seconds ====================
Lưu ý: Mặc định, pytest sẽ escape các ký tự không phải ASCII trong chuỗi Unicode để tham số hóa vì một số hạn chế. Tuy nhiên, nếu bạn muốn hiển thị nguyên văn các chuỗi Unicode trong terminal, hãy thêm tùy chọn này vào
pytest.ini: (Yêu cầu pytest>=5.0.0 để hiển thị tiếng Trung trong dữ liệu hoặc ids)
[pytest]
disable_test_id_escaping_and_forfeit_all_rights_to_community_support = True
Nhưng cần lưu ý rằng điều này có thể gây ra lỗi phụ thuộc vào hệ điều hành và plugin đang sử dụng.
Trong ví dụ trên, chỉ có một bộ giá trị đầu vào/đầu ra không qua test. Bạn có thể thấy giá trị input/output trong traceback như bình thường.
Bạn cũng có thể áp dụng tham số hóa cho class hoặc module (xem thêm tài liệu về đánh dấu test function bằng thuộc tính), lúc này các hàm sẽ được gọi với từng bộ tham số.
Có thể đánh dấu từng test instance cụ thể bằng mark.xfail:
# nội dung của test_expectation.py
import pytest
@pytest.mark.parametrize(
"test_input,expected",
[("3+5",8),("2+4",6),pytest.param("6*9",42,marks=pytest.mark.xfail)],
)
def test_eval(test_input,expected):
assert eval(test_input) == expected
Kết quả khi chạy:
$ pytest
=========================== test session starts ============================
platform linux -- Python 3.x.y,pytest-4.x.y,py-1.x.y,pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 3 items
test_expectation.py ..x [100%]
=================== 2 passed,1 xfailed in 0.12 seconds ====================
Bộ tham số trước gây lỗi giờ được ghi nhận là "xfailed (Expected failure)".
Nếu danh sách tham số từ parametrize bị rỗng (ví dụ do hàm động), hành vi của pytest sẽ tuân theo tùy chọn empty_parameter_set_mark.
Để chạy tất cả tổ hợp tham số, có thể chồng các decorator parametrize:
import pytest
@pytest.mark.parametrize("x",[0,1])
@pytest.mark.parametrize("y",[2,3])
def test_foo(x,y):
pass
Điều này sẽ tạo 4 test case với các tổ hợp (x=0/y=2, x=1/y=2, x=0/y=3, x=1/y=3) theo thứ tự decorator.
Ví dụ cơ bản về pytest_generate_tests
Khi cần định nghĩa lược đồ tham số hóa tùy chỉnh hoặc xác định động tham số cho Fixture, bạn có thể sử dụng hook pytest_generate_tests. Thông qua đối tượng metafunc, bạn có thể kiểm tra ngữ cảnh test và gọi metafunc.parametrize() để tham số hóa.
Ví dụ, giả sử muốn chạy test với chuỗi đầu vào được cung cấp qua tùy chọn dòng lệnh mới của pytest. Trước tiên viết test đơn giản:
# nội dung của test_strings.py
def test_valid_string(stringinput):
assert stringinput.isalpha()
Thêm file conftest.py với tùy chọn dòng lệnh và logic tham số hóa:
# nội dung của conftest.py
def pytest_addoption(parser):
parser.addoption(
"--stringinput",
action="append",
default=[],
help="danh sách chuỗi truyền vào cho test functions",
)
def pytest_generate_tests(metafunc):
if "stringinput" in metafunc.fixturenames:
metafunc.parametrize("stringinput",metafunc.config.getoption("stringinput"))
Khi cung cấp 2 giá trị stringinput, test sẽ chạy 2 lần:
$ pytest -q --stringinput="hello" --stringinput="world" test_strings.py
.. [100%]
2 passed in 0.12 seconds
Chạy test với stringinput gây lỗi:
$ pytest -q --stringinput="!" test_strings.py
F [100%]
================================= FAILURES =================================
___________________________ test_valid_string[!] ___________________________
stringinput = '!'
def test_valid_string(stringinput):
> assert stringinput.isalpha()
E AssertionError: assert False
E + where False = <built-in method isalpha of str object at 0xdeadbeef>()
E + where <built-in method isalpha of str object at 0xdeadbeef> = '!'.isalpha
test_strings.py:4: AssertionError
1 failed in 0.12 seconds
Nếu không cung cấp stringinput, test sẽ bị bỏ qua:
$ pytest -q -rs test_strings.py
s [100%]
========================= short test summary info ==========================
SKIPPED [1] test_strings.py: got empty parameter set ['stringinput'],function test_valid_string at $REGENDOC_TMPDIR/test_strings.py:2
1 skipped in 0.12 seconds
Lưu ý: Khi gọi
metafunc.parametrizenhiều lần với các tập tham số khác nhau, tên tham số trong các tập phải không trùng lặp, ngược lại sẽ gây lỗi.
Các ví dụ mở rộng
Xem thêm các ví dụ tham số hóa khác tại tài liệu chính thức của Pytest.