๊ฐ์
parametrize๋ pytest์์ ํ ์คํธ ์ฝ๋์ ์ค๋ณต์ ์ค์ด๊ณ ๋ค์ํ ์ ๋ ฅ๊ฐ์ ๋ํ ํ ์คํธ๋ฅผ ๊ฐ๊ฒฐํ๊ฒ ์์ฑํ ์ ์๋๋ก ๋์์ฃผ๋ ์ ์ฉํ ๊ธฐ๋ฅ์ ๋๋ค. ํ๋์ ํ ์คํธ ํจ์๋ก ์ฌ๋ฌ ์ ๋ ฅ ์กฐํฉ์ ๋ํด ๋ ๋ฆฝ์ ์ธ ํ ์คํธ๋ฅผ ์คํํ ์ ์์ด, ๋น์ทํ ํจํด์ ํ ์คํธ๋ฅผ ๋ฐ๋ณต ์์ฑํ๋ ๋์ ๊ฐ๊ฒฐํ๊ณ ๋ช ํํ ์ฝ๋๋ก ๋ค์ํ ์๋๋ฆฌ์ค๋ฅผ ์ปค๋ฒํ ์ ์์ต๋๋ค.
์ฃผ์ ์ฅ์
- ์ฝ๋ ์ค๋ณต ๊ฐ์: ์ฌ๋ฌ ์ ๋ ฅ๊ฐ์ ํ๋์ ํ ์คํธ ํจ์๋ก ์ฒ๋ฆฌ
- ๊ฐ๋ ์ฑ ํฅ์: ๋ค์ํ ์ ๋ ฅ๊ณผ ๊ธฐ๋ ๊ฒฐ๊ณผ๋ฅผ ๋ช ํํ๊ฒ ํํ
- ์ ์ง๋ณด์ ์ฉ์ด์ฑ: ํ ์คํธ ์ผ์ด์ค ์ถ๊ฐ ๋ฐ ์์ ์ด ๊ฐํธ
- ํ ์คํธ ์ปค๋ฒ๋ฆฌ์ง ํ๋: ๋ค์ํ ์ ๋ ฅ ์กฐํฉ ํ ์คํธ ๊ฐ๋ฅ
๊ธฐ๋ณธ ์ฌ์ฉ๋ฒ
@pytest.mark.parametrize("์ธ์๋ช
", [๊ฐ1, ๊ฐ2, ๊ฐ3])
def test_ํจ์๋ช
(์ธ์๋ช
):
# ํ
์คํธ ๋ก์ง
assert ...์ฃผ์ ๊ธฐ๋ฅ
1. ์ฌ๋ฌ ์ธ์ parametrize
@pytest.mark.parametrize("a, b, expected", [
(1, 2, 3),
(5, 5, 10),
(-1, 1, 0),
(0, 0, 0),
(100, 200, 300)
])
def test_add(a, b, expected):
assert add(a, b) == expected์คํ ๊ฒฐ๊ณผ:
test_math.py::test_add[1-2-3] PASSED
test_math.py::test_add[5-5-10] PASSED
test_math.py::test_add[-1-1-0] PASSED
test_math.py::test_add[0-0-0] PASSED
test_math.py::test_add[100-200-300] PASSED
๋ณด์ถฉ ์ค๋ช
: ์ฌ๋ฌ ์ธ์๋ฅผ ํ ๋ฒ์ parametrize ํ ์ ์์ผ๋ฉฐ, ๊ฐ ํํ์ ํ๋์ ํ
์คํธ ์ผ์ด์ค๊ฐ ๋ฉ๋๋ค. ์ ์์์์๋ 5๊ฐ์ ์ธ์ ์กฐํฉ์ ๋ํด test_add ํจ์๊ฐ 5๋ฒ ๋
๋ฆฝ์ ์ผ๋ก ์คํ๋ฉ๋๋ค. pytest๋ ์๋์ผ๋ก ๊ฐ ์ผ์ด์ค์ ๋ํ ํ
์คํธ ID๋ฅผ ์์ฑํฉ๋๋ค(์: [1-2-3]).
2. ํ ์คํธ ์๋ณ์ฑ ๋์ด๊ธฐ (ids ์ฌ์ฉ)
@pytest.mark.parametrize("a, b, expected", [
(1, 2, 3),
(5, 5, 10),
(-1, 1, 0),
(0, 0, 0)
], ids=["simple_add", "double_five", "add_neg_pos", "add_zeros"])
def test_add_with_custom_ids(a, b, expected):
assert add(a, b) == expected์คํ ๊ฒฐ๊ณผ:
test_math_with_ids.py::test_add_with_custom_ids[simple_add] PASSED
test_math_with_ids.py::test_add_with_custom_ids[double_five] PASSED
test_math_with_ids.py::test_add_with_custom_ids[add_neg_pos] PASSED
test_math_with_ids.py::test_add_with_custom_ids[add_zeros] PASSED
๋ณด์ถฉ ์ค๋ช
: ํ
์คํธ ์ผ์ด์ค๊ฐ ๋ง์์ง์๋ก ์๋ ์์ฑ๋ ID([1-2-3])๋ง์ผ๋ก๋ ์ด๋ค ํ
์คํธ์ธ์ง ํ์
ํ๊ธฐ ์ด๋ ค์ธ ์ ์์ต๋๋ค. ids ํ๋ผ๋ฏธํฐ๋ฅผ ์ฌ์ฉํ๋ฉด ๊ฐ ํ
์คํธ ์ผ์ด์ค์ ์๋ฏธ ์๋ ์ด๋ฆ์ ๋ถ์ฌํ ์ ์์ด ํ
์คํธ ๊ฒฐ๊ณผ๋ฅผ ํด์ํ๊ณ ๋๋ฒ๊น
ํ๊ธฐ ์ฌ์์ง๋๋ค. ํนํ ํ
์คํธ๊ฐ ์คํจํ์ ๋ ์ด๋ค ์ผ์ด์ค์์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋์ง ๋น ๋ฅด๊ฒ ํ์ธํ ์ ์์ต๋๋ค.
3. pytest.param ํ์ฉ
@pytest.mark.parametrize("a, b, expected", [
pytest.param(1, 2, 3, id="positive_numbers_check"),
pytest.param(5, 5, 10, id="equal_numbers_check"),
pytest.param(-1, 1, 0, id="positive_and_negative_check"),
pytest.param(0, 0, 0, id="zeros_check"),
])
def test_add_with_pytest_param_ids(a, b, expected):
assert add(a, b) == expected์คํ ๊ฒฐ๊ณผ:
test_math_with_ids.py::test_add_with_pytest_param_ids[positive_numbers_check] PASSED
test_math_with_ids.py::test_add_with_pytest_param_ids[equal_numbers_check] PASSED
test_math_with_ids.py::test_add_with_pytest_param_ids[positive_and_negative_check] PASSED
test_math_with_ids.py::test_add_with_pytest_param_ids[zeros_check] PASSED
๋ณด์ถฉ ์ค๋ช
: pytest.param์ ์ฌ์ฉํ๋ฉด ๊ฐ ํ
์คํธ ์ผ์ด์ค๋ง๋ค ID๋ฟ๋ง ์๋๋ผ ๋ค๋ฅธ pytest ๋งํฌ(marks)๋ ๊ฐ๋ณ์ ์ผ๋ก ์ ์ฉํ ์ ์์ต๋๋ค. ์ด๋ ๋จ์ํ ID๋ฅผ ๋ถ์ฌํ๋ ๊ฒ๋ณด๋ค ๋ ๋ง์ ์ ์ด๊ฐ ํ์ํ ๋ ์ ์ฉํฉ๋๋ค. ์๋ฅผ ๋ค์ด, ํน์ ์ผ์ด์ค๋ง ๊ฑด๋๋ฐ๊ฑฐ๋(skip) ์คํจ๊ฐ ์์๋๋ ์ผ์ด์ค(xfail)๋ฅผ ํ์ํ ์ ์์ต๋๋ค.
4. ์ฌ๋ฌ parametrize ๋ฐ์ฝ๋ ์ดํฐ ์กฐํฉ
@pytest.mark.parametrize("x", [0, 1])
@pytest.mark.parametrize("y", [2, 3])
def test_foo(x, y):
print(f"x: {x}, y: {y}")
assert True์คํ ๊ฒฐ๊ณผ:
test_multiple_parametrize.py::test_foo[2-0] PASSED
test_multiple_parametrize.py::test_foo[3-0] PASSED
test_multiple_parametrize.py::test_foo[2-1] PASSED
test_multiple_parametrize.py::test_foo[3-1] PASSED
๋ณด์ถฉ ์ค๋ช
: ํ๋์ ํ
์คํธ ํจ์์ ์ฌ๋ฌ parametrize ๋ฐ์ฝ๋ ์ดํฐ๋ฅผ ์ ์ฉํ๋ฉด, pytest๋ ๋ชจ๋ ๊ฐ๋ฅํ ์กฐํฉ(๋ฐ์นด๋ฅดํธ ๊ณฑ)์ ์์ฑํ์ฌ ํ
์คํธ๋ฅผ ์คํํฉ๋๋ค. ์ ์์์์๋ x์ 2๊ฐ์ง ๊ฐ๊ณผ y์ 2๊ฐ์ง ๊ฐ์ ์กฐํฉ์ผ๋ก ์ด 4๋ฒ์ ํ
์คํธ๊ฐ ์คํ๋ฉ๋๋ค. ์ด ๋ฐฉ์์ ๋ ๊ฐ ์ด์์ ๋
๋ฆฝ์ ์ธ ๋ณ์๋ค์ ๋ชจ๋ ์กฐํฉ์ ํ
์คํธํด์ผ ํ ๋ ๋งค์ฐ ์ ์ฉํฉ๋๋ค. ๋ฐ์ฝ๋ ์ดํฐ ์์์ ๋ฐ๋ผ ํ
์คํธ ID์ ํ๋ผ๋ฏธํฐ ์์๊ฐ ๊ฒฐ์ ๋๋ฏ๋ก ์ฃผ์ํด์ผ ํฉ๋๋ค.
5. fixture parametrize (indirect=True)
@pytest.fixture
def my_fixture(request):
# request.param์ parametrize์์ ์ ๋ฌ๋ ๊ฐ์
๋๋ค
return request.param * 10
@pytest.mark.parametrize("my_fixture", [1, 2, 3], indirect=True)
def test_indirect_fixture(my_fixture):
assert my_fixture % 10 == 0
if my_fixture == 10:
assert True
elif my_fixture == 20:
assert True
elif my_fixture == 30:
assert True์คํ ๊ฒฐ๊ณผ:
test_indirect_parametrize.py::test_indirect_fixture[1] PASSED
test_indirect_parametrize.py::test_indirect_fixture[2] PASSED
test_indirect_parametrize.py::test_indirect_fixture[3] PASSED
๋ณด์ถฉ ์ค๋ช
: indirect=True ์ต์
์ ์ฌ์ฉํ๋ฉด parametrize๋ ๊ฐ์ ์ง์ ํ
์คํธ ํจ์์ ์ธ์๋ก ์ ๋ฌํ๋ ๋์ , ๊ฐ์ ์ด๋ฆ์ fixture์ ์ ๋ฌํ ์ ์์ต๋๋ค. ์ด ๊ฒฝ์ฐ fixture ํจ์ ๋ด์์ request.param์ ํตํด parametrize๋ ๊ฐ์ ์ ๊ทผํ ์ ์์ต๋๋ค.
์ด ๋ฐฉ์์ ๋ค์๊ณผ ๊ฐ์ ๊ฒฝ์ฐ์ ์ ์ฉํฉ๋๋ค:
- ํ ์คํธ ์คํ ์ ์ ๋ณต์กํ ์ค์ ์ด ํ์ํ ๊ฒฝ์ฐ
- parametrize๋ ๊ฐ์ ๊ฐ๊ณตํด์ ์ฌ์ฉํด์ผ ํ๋ ๊ฒฝ์ฐ
- ๋์ผํ ํ ์คํธ์ ๋ํด ์๋ก ๋ค๋ฅธ ํ๊ฒฝ ์ค์ ์ด ํ์ํ ๊ฒฝ์ฐ
๋ํ indirect=['param1', 'param2'] ํํ๋ก ์ฌ์ฉํ์ฌ ์ฌ๋ฌ ํ๋ผ๋ฏธํฐ ์ค ์ผ๋ถ๋ง ๊ฐ์ ์ฒ๋ฆฌํ ์๋ ์์ต๋๋ค.
6. ๋ค๋ฅธ ๋งํฌ์ ํจ๊ป ์ฌ์ฉ
@pytest.mark.parametrize("value, expected_type", [
(10, int),
("hello", str),
pytest.param(5.5, float, marks=pytest.mark.skip(reason="๋ถ๋ ์์์ ํ
์คํธ๋ ํ์ฌ ๊ฑด๋๋")),
pytest.param([1, 2], list, marks=pytest.mark.xfail(reason="๋ฆฌ์คํธ ํ์
์ ์คํจ ์์"))
])
def test_types(value, expected_type):
assert isinstance(value, expected_type)์คํ ๊ฒฐ๊ณผ:
test_parametrize_with_marks.py::test_types[10-<class 'int'>] PASSED
test_parametrize_with_marks.py::test_types[hello-<class 'str'>] PASSED
test_parametrize_with_marks.py::test_types[5.5-<class 'float'>] SKIPPED (๋ถ๋ ์์์ ํ
์คํธ๋ ํ์ฌ ๊ฑด๋๋)
test_parametrize_with_marks.py::test_types[[1, 2]-<class 'list'>] xfail (๋ฆฌ์คํธ ํ์
์ ์คํจ ์์)
๋ณด์ถฉ ์ค๋ช
: pytest.param๊ณผ ํจ๊ป ๋ค์ํ pytest ๋งํฌ๋ฅผ ์ฌ์ฉํ๋ฉด ํน์ ํ
์คํธ ์ผ์ด์ค์ ๋ํด ์ธ๋ฐํ ์ ์ด๊ฐ ๊ฐ๋ฅํฉ๋๋ค:
pytest.mark.skip: ํน์ ์ผ์ด์ค๋ฅผ ๊ฑด๋๋ฐ๋๋ก ์ง์ pytest.mark.xfail: ์คํจ๊ฐ ์์๋๋ ์ผ์ด์ค๋ฅผ ํ์ (ํ ์คํธ๋ ์คํ๋์ง๋ง ์คํจํด๋ ์ ์ฒด ํ ์คํธ ๊ฒฐ๊ณผ์ ์ํฅ์ ์ฃผ์ง ์์)pytest.mark.parametrize: ์ด ์์ ์์์ฒ๋ผ ๋ค๋ฅธ ๋งํฌ์ ํจ๊ป ์ฌ์ฉ ๊ฐ๋ฅ
์ด๋ฅผ ํตํด ๊ฐ๋ฐ ์ค์ธ ๊ธฐ๋ฅ์ด๋ ์์ง ๊ตฌํ๋์ง ์์ ๋ถ๋ถ์ ๋ํ ํ ์คํธ๋ฅผ ๋ฏธ๋ฆฌ ์์ฑํ๊ณ , ์ ์ ํ ํ์ํ ์ ์์ต๋๋ค. ๋ํ ํน์ ํ๊ฒฝ์ด๋ ์กฐ๊ฑด์์๋ง ์คํํด์ผ ํ๋ ํ ์คํธ๋ฅผ ๊ด๋ฆฌํ๊ธฐ์๋ ์ข์ต๋๋ค.
์ถ๊ฐ ํ
ํ๋ผ๋ฏธํฐ ๊ฐ ์์ฑ์ ์ํ ํจ์ ์ฌ์ฉ
def generate_test_data():
return [(i, i*2) for i in range(5)]
@pytest.mark.parametrize("input, expected", generate_test_data())
def test_double(input, expected):
assert input * 2 == expected๋งค๊ฐ๋ณ์ํ๋ ํด๋์ค
@pytest.mark.parametrize("value", [1, 2, 3])
class TestClass:
def test_one(self, value):
assert value >= 1
def test_two(self, value):
assert value <= 3๊ฒฐ๋ก
pytest.mark.parametrize๋ ํ ์คํธ ์ฝ๋์ ํจ์จ์ฑ๊ณผ ๊ฐ๋ ์ฑ์ ํฌ๊ฒ ํฅ์์ํค๋ฉฐ, ๋ค์ํ ์ ๋ ฅ๊ฐ์ ๋ํ ํ ์คํธ๋ฅผ ๊ฐ๊ฒฐํ๊ฒ ์์ฑํ ์ ์๊ฒ ํด์ฃผ๋ ๊ฐ๋ ฅํ ๋๊ตฌ์ ๋๋ค. ์ค๋ณต ์ฝ๋๋ฅผ ์ค์ด๊ณ , ํ ์คํธ ์ผ์ด์ค ๊ด๋ฆฌ๊ฐ ์ฉ์ดํ๋ฉฐ, ๋ค์ํ ์๋๋ฆฌ์ค์ ๋ํ ํ ์คํธ ์ปค๋ฒ๋ฆฌ์ง๋ฅผ ํ๋ํ ์ ์์ต๋๋ค. ํนํ ์ฌ๋ฌ ์ ๋ ฅ ์กฐํฉ์ ๋ํด ๋์ผํ ๋ก์ง์ ๋ฐ๋ณต์ ์ผ๋ก ํ ์คํธํด์ผ ํ๋ ๊ฒฝ์ฐ ์ฌ์ฉํ๋ฉด ๋งค์ฐ ํจ๊ณผ์ ์ ๋๋ค.