กลับมาอีกครั้งกับซีรีส์ วัยรุ่นเทสดี หลังจากตอนที่แล้วเราได้รู้จักกับ pytest และเขียนเทสแรกกันไปแล้ว วันนี้เราจะมาลงลึกกันเรื่อง Assertions ที่ทรงพลังกว่าแค่ assert a == b
และเทคนิคการจัดระเบียบเทสให้เป็นระบบเหมือนนักพัฒนามืออาชีพ!
คราวนี้เราจะเห็น pytest ในแง่มุมที่เจ๋งและใช้งานได้จริงมากขึ้น พร้อมทั้งเรียนรู้วิธีการเขียนเทสที่ maintainable และ scalable 🎯
Assertions คืออะไร?
Assertions คือการตรวจสอบเงื่อนไขภายในโค้ดด้วยคำสั่ง assert โดยทั่วไปแล้วจะใช้ในระหว่างการทดสอบเพื่อยืนยันว่า ผลลัพธ์ที่ได้ตรงตามที่คาดหวังหรือไม่ ถ้าเงื่อนไขใน assert เป็น False โปรแกรมจะหยุดทำงานและแสดงข้อผิดพลาด
Assert ไม่ได้มีแค่ == เท่านั้นนะ!
หลายคนอาจคิดว่า assert ก็แค่เช็คว่าค่าเท่ากันหรือไม่ แต่จริงๆ แล้วเราสามารถใช้ assert ได้หลากหลายมากขึ้น มาดูกันว่า pytest รองรับอะไรบ้าง
1. การเปรียบเทียบพื้นฐาน
# test_assertions_basic.py
def test_equality_assertions():
# เทสการเปรียบเทียบความเท่ากัน
assert 2 + 2 == 4
assert "hello" == "hello"
assert [1, 2, 3] == [1, 2, 3]
def test_inequality_assertions():
# เทสการเปรียบเทียบความไม่เท่ากัน
assert 5 != 3
assert 10 > 5
assert 3 < 7
assert 5 >= 5
assert 5 <= 10
def test_boolean_assertions():
# เทสค่า Boolean
assert True
assert not False
assert bool([1, 2, 3]) # list ที่มีข้อมูลจะเป็น True
assert not bool([]) # list ว่างจะเป็น False
2. Membership
def test_membership():
# เทสการตรวจสอบสมาชิกภาพ"""
fruits = ["apple", "banana", "orange"]
# เช็คว่ามีสมาชิกหรือไม่
assert "apple" in fruits
assert "mango" not in fruits
# เช็คใน string
assert "test" in "pytest is the best"
assert "java" not in "python is awesome"
def test_substring_checking():
# เทสการเช็ค substring
message = "Hello World"
assert "Hello" in message
assert "World" in message
assert "Python" not in message
3. ตรวจสอบชนิดของตัวแปร
def test_type_checking():
# เทสการตรวจสอบ type
assert isinstance(42, int)
assert isinstance("hello", str)
assert isinstance([1, 2, 3], list)
assert isinstance({"key": "value"}, dict)
def test_type_validation():
# เทสการ validate type แบบเฉพาะเจาะจง
# ฟังก์ชันที่ต้องการ return type เฉพาะ
def get_user_age() -> int:
return 25
result = get_user_age()
assert isinstance(result, int) # ตรวจสอบว่าตัวแปรเป็น int
assert result > 0 # ตรวจสอบว่าตัวแปรมีค่ามากกว่า 0
4. การตวจสอบ Exception
เราสามารถใช้ pytest.raises เพื่อเช็คว่า exception ที่ต้องการให้ raise เมื่อมีความผิดปกติบางอย่างภายใน function ถูก raise ขึ้นมาหรือไม่
import pytest
def test_exception_with_message():
"""เทสการเช็ค Exception พร้อมการตรวจสอบ message"""
with pytest.raises(ValueError, match="Cannot divide by zero"):
divide_numbers(10, 0)
def test_exception_type_only():
"""เทสการเช็ค Exception เฉพาะ type"""
with pytest.raises(TypeError):
divide_numbers("10", 5)
def test_exception_capture():
"""เทสการ capture exception เพื่อตรวจสอบรายละเอียดอื่นๆ ที่เกี่ยวข้อง"""
with pytest.raises(ValueError) as exc_info:
divide_numbers(10, 0)
# ตรวจสอบ message ของ exception
assert "Cannot divide by zero" in str(exc_info.value)
แก้ไข Project ให้มีการ raise exception
ขั้นตอนการ setup project จะอยู่ใน part1
แก้ไขไฟล์ src/calculator.py เพิ่มการตรวจสอบ input ในฟังก์ชันต่างๆ ของ calculator และ raise exception เมื่อ input ไม่ถูกต้อง
# src/calculator.py
def add(x,y):
if not isinstance(x, (int, float)) or not isinstance(y, (int, float)):
raise TypeError("Both input must be number")
return x + y
def subtract(x,y):
if not isinstance(x, (int, float)) or not isinstance(y, (int, float)):
raise TypeError("Both input must be number")
return x - y
def multiply(x,y):
if not isinstance(x, (int, float)) or not isinstance(y, (int, float)):
raise TypeError("Both input must be number")
return x * y
def divide(num,den):
if den == 0:
raise ZeroDivisionError("Denominator cannot be zero")
if not isinstance(num, (int, float)) or not isinstance(den, (int, float)):
raise TypeError("Both input must be number")
return num / den
แก้ไข Tests ให้รองรับ Exception
เราจะแก้ไข file test/test_calculator.py โดยจะเพิ่ม test case
# tests/test_calculator.py
from src.calculator import add, subtract, multiply, divide
import pytest
def test_add_positive_numbers():
assert add(1, 2) == 3
assert add(10, 5) == 15
assert add(0, 0) == 0
def test_add_decimal_numbers():
assert add(1.1, 2.5) == 3.6
assert add(10.1, 5.95) == 16.05
assert add(0.0, 0.0) == 0.0
assert add(1.25, 2.13) == 3.38
def test_add_negative_numbers():
assert add(-1, -1) == -2
assert add(-10, -5) == -15
assert add(-500, -5) == -505
def test_add_mixed_numbers():
assert add(-10, 3) == -7
assert add(5, -3) == 2
def test_add_non_numeric_input():
with pytest.raises(TypeError, match="Both input must be number"):
add("1", 2)
with pytest.raises(TypeError, match="Both input must be number"):
add(1, "2")
with pytest.raises(TypeError, match="Both input must be number"):
add("1", "2")
def test_subtract_positive_numbers():
assert subtract(5, 2) == 3
assert subtract(10, 5) == 5
assert subtract(7, 25) == -18
def test_subtract_negative_numbers():
assert subtract(-5, -2) == -3
assert subtract(-10, -5) == -5
assert subtract(-7, -25) == 18
def test_subtract_mixed_numbers():
assert subtract(-5, 2) == -7
assert subtract(5, -2) == 7
def test_substract_decimal_numbers():
assert subtract(5.5, 2.2) == 3.3
assert subtract(10.1, 5.95) == 4.15
assert subtract(0.0, 0.0) == 0.0
def test_subtract_non_numeric_input():
with pytest.raises(TypeError, match="Both input must be number"):
subtract("5", 2)
with pytest.raises(TypeError, match="Both input must be number"):
subtract(5, "2")
with pytest.raises(TypeError, match="Both input must be number"):
subtract("5", "2")
def test_multiply_positive_numbers():
assert multiply(2, 3) == 6
assert multiply(5, 4) == 20
assert multiply(0, 5) == 0
def test_multiply_negative_numbers():
assert multiply(-2, -3) == 6
assert multiply(-5, -4) == 20
assert multiply(-2, -4) == 8
def test_multiply_mixed_numbers():
assert multiply(-2, 3) == -6
assert multiply(2, -3) == -6
assert multiply(0, 5) == 0
def test_multiply_decimal_numbers():
assert multiply(2.5, 4) == 10
assert multiply(3.1, 2.0) == 6.2
assert multiply(0.0, 5) == 0.0
def test_multiply_non_numeric_input():
with pytest.raises(TypeError, match="Both input must be number"):
multiply("2", 3)
with pytest.raises(TypeError, match="Both input must be number"):
multiply(2, "3")
with pytest.raises(TypeError, match="Both input must be number"):
multiply("2", "3")
def test_divide_positive_numbers():
assert divide(6, 2) == 3
assert divide(5, 2) == 2.5
def test_divide_negative_numbers():
assert divide(-6, -2) == 3
assert divide(-5, -2) == 2.5
def test_divide_mixed_numbers():
assert divide(-6, 2) == -3
assert divide(6, -2) == -3
assert divide(0, 5) == 0
def test_divide_decimal_numbers():
assert divide(5.5, 2.2) == 2.5
assert divide(10.1, 5.05) == 2.0
assert divide(0.0, 1.0) == 0.0
def test_divide_by_zero():
with pytest.raises(ZeroDivisionError, match="Denominator cannot be zero"):
divide(5, 0)
with pytest.raises(ZeroDivisionError, match="Denominator cannot be zero"):
divide(0, 0)
def test_divide_non_numeric_input():
with pytest.raises(TypeError, match="Both input must be number"):
divide("6", 2)
with pytest.raises(TypeError, match="Both input must be number"):
divide(6, "2")
with pytest.raises(TypeError, match="Both input must be number"):
divide("6", "2")
ลองรันเทสด้วยคำสั่ง: pytest -v
จะเห็นว่ามีเทสที่พังด้วย ชิบหายแล้ว! 😱 มันเกิดจากอะไรลองมาดูกัน

ทำไม 10.1 – 5.95 ถึงไม่เท่ากับ 4.15?
เพราะการเก็บข้อมูลเลขทศนิยมในคอมพิวเตอร์มีความแม่นยำไม่สมบูรณ์ เราจึงต้องใช้การเช็คความใกล้เคียงแทนการเทียบเท่าตรงๆ
ปัญหานี้เรียกว่า floating-point precision issue ส่วนใครอยากเข้าใจเรื่องนี้ให้ลึกซึ้งขึ้น แนะนำให้ไปอ่านที่บทความนี้ได้เลย “ทำไมคอมพิวเตอร์บวกทศนิยมผิด“
เราจะแก้ปัญหานี้โดยการใช้ pytest.approx
ซึ่งเป็นฟังก์ชันที่ช่วยให้เราสามารถเช็คความใกล้เคียงของค่าทศนิยมได้
def test_substract_decimal_numbers():
assert subtract(5.5, 2.2) == pytest.approx(3.3)
assert subtract(10.1, 5.95) == pytest.approx(4.15)
assert subtract(0.0, 0.0) == 0.0
หลังจากแก้ไขแล้วลองรันเทสใหม่อีกครั้ง จะเห็นว่าเทสทั้งหมดผ่านเรียบร้อยแล้ว! 🎉

การจัดระเบียบเทสแบบมืออาชีพ
จากตัวอย่างที่ผ่านมาเราจะเริ่มเห็นว่า เทสที่เราเขียนมันชักจะเริ่มยาวและเยอะแล้วหล่ะสิ แล้วจะมีวิธีการจัดระเบียบโค้ดพวกนี้อย่างไร
1. โครงสร้างไฟล์ที่ดี
wai_roon_test_dee/
├── calculator/
│ ├── basic_operations.py
│ └── advanced_operations.py
├── tests/
│ ├── test_basic_operations.py
│ ├── test_advanced_operations.py
│ └── conftest.py # ไฟล์สำหรับ shared fixtures
├── requirements.txt
└── pytest.ini # ไฟล์ config ของ pytest
สำหรับโปรเจคขนาดใหญ่ การจัดระเบียบโครงสร้างไฟล์ให้เหมาะสมจะช่วยให้การดูแลรักษาโค้ดทำได้ง่ายขึ้น ตอนนี้โปรเจคเรายังไม่ถือว่าใหญ่ อาจจะไม่เห็นความจำเป็นในการแยกไฟล์เทส แต่เมื่อโปรเจคเริ่มโตขึ้น การแยกไฟล์และโฟลเดอร์จะช่วยให้เราจัดการได้ง่ายขึ้น
2. การใช้คลาสจัดกลุ่มเทส
เราสามารถสร้างคลาส สำหรับแต่ละกลุ่มของเทส TestAddition
, TestSubtraction
, TestMultiplication
, และ TestDivision
เพื่อให้โค้ดดูเป็นระเบียบและเข้าใจง่ายขึ้น
# tests/test_calculator.py
from src.calculator import add, subtract, multiply, divide
import pytest
class TestAddition:
"""Test cases for addition function"""
def test_positive_numbers(self):
assert add(1, 2) == 3
assert add(10, 5) == 15
assert add(0, 0) == 0
def test_decimal_numbers(self):
assert add(1.1, 2.5) == pytest.approx(3.6)
assert add(10.1, 5.95) == pytest.approx(16.05)
assert add(0.0, 0.0) == pytest.approx(0.0)
assert add(1.25, 2.13) == pytest.approx(3.38)
def test_negative_numbers(self):
assert add(-1, -1) == -2
assert add(-10, -5) == -15
assert add(-500, -5) == -505
def test_mixed_numbers(self):
assert add(-10, 3) == -7
assert add(5, -3) == 2
def test_non_numeric_input(self):
with pytest.raises(TypeError, match="Both input must be number"):
add("1", 2)
with pytest.raises(TypeError, match="Both input must be number"):
add(1, "2")
with pytest.raises(TypeError, match="Both input must be number"):
add("1", "2")
class TestSubtraction:
"""Test cases for subtraction function"""
def test_positive_numbers(self):
assert subtract(5, 2) == 3
assert subtract(10, 5) == 5
assert subtract(7, 25) == -18
def test_negative_numbers(self):
assert subtract(-5, -2) == -3
assert subtract(-10, -5) == -5
assert subtract(-7, -25) == 18
def test_mixed_numbers(self):
assert subtract(-5, 2) == -7
assert subtract(5, -2) == 7
def test_decimal_numbers(self):
assert subtract(5.5, 2.2) == pytest.approx(3.3)
assert subtract(10.1, 5.95) == pytest.approx(4.15)
assert subtract(0.0, 0.0) == 0.0
def test_non_numeric_input(self):
with pytest.raises(TypeError, match="Both input must be number"):
subtract("5", 2)
with pytest.raises(TypeError, match="Both input must be number"):
subtract(5, "2")
with pytest.raises(TypeError, match="Both input must be number"):
subtract("5", "2")
class TestMultiplication:
"""Test cases for multiplication function"""
def test_positive_numbers(self):
assert multiply(2, 3) == 6
assert multiply(5, 4) == 20
assert multiply(0, 5) == 0
def test_negative_numbers(self):
assert multiply(-2, -3) == 6
assert multiply(-5, -4) == 20
assert multiply(-2, -4) == 8
def test_mixed_numbers(self):
assert multiply(-2, 3) == -6
assert multiply(2, -3) == -6
assert multiply(0, 5) == 0
def test_decimal_numbers(self):
assert multiply(2.5, 4) == pytest.approx(10.0)
assert multiply(3.1, 2.0) == pytest.approx(6.2)
assert multiply(0.0, 5) == 0.0
def test_non_numeric_input(self):
with pytest.raises(TypeError, match="Both input must be number"):
multiply("2", 3)
with pytest.raises(TypeError, match="Both input must be number"):
multiply(2, "3")
with pytest.raises(TypeError, match="Both input must be number"):
multiply("2", "3")
class TestDivision:
"""Test cases for division function"""
def test_positive_numbers(self):
assert divide(6, 2) == 3
assert divide(5, 2) == 2.5
def test_negative_numbers(self):
assert divide(-6, -2) == 3
assert divide(-5, -2) == 2.5
def test_mixed_numbers(self):
assert divide(-6, 2) == -3
assert divide(6, -2) == -3
assert divide(0, 5) == 0
def test_decimal_numbers(self):
assert divide(5.5, 2.2) == pytest.approx(2.5)
assert divide(10.1, 5.05) == pytest.approx(2.0)
assert divide(0.0, 1.0) == pytest.approx(0.0)
def test_by_zero(self):
with pytest.raises(ZeroDivisionError, match="Denominator cannot be zero"):
divide(5, 0)
with pytest.raises(ZeroDivisionError, match="Denominator cannot be zero"):
divide(0, 0)
def test_non_numeric_input(self):
with pytest.raises(TypeError, match="Both input must be number"):
divide("6", 2)
with pytest.raises(TypeError, match="Both input must be number"):
divide(6, "2")
with pytest.raises(TypeError, match="Both input must be number"):
divide("6", "2")

ดูผลลัพธ์จาก test explorer เราจะเห็นว่าเทสถูกแบ่งออกเป็นกลุ่มๆ ตามฟังก์ชันที่ทดสอบ
3. การใช้ pytest.mark.parametrize
ลองสังเกตุดูจะเห็นว่ามีการเรียกใช้ assert ซ้ำๆกันอยู่ เรามาลองใช้ pytest.mark.parametrize
เพื่อทำให้โค้ดดูเรียบร้อยขึ้นกัน
แก้ไขไฟล์ test/test_calculator.py
# tests/test_calculator.py
from src.calculator import add, subtract, multiply, divide
import pytest
class TestAddition:
"""Test cases for addition function"""
@pytest.mark.parametrize("input1, input2, expected", [
(1, 2, 3), # Test case 1
(10, 5, 15), # Test case 2
(0, 0, 0) # Test case 3
])
def test_positive_numbers(self, input1, input2, expected):
assert add(input1, input2) == expected
@pytest.mark.parametrize("input1, input2, expected", [
(1.1, 2.5, 3.6),
(10.1, 5.95, 16.05),
(0.0, 0.0, 0.0),
(1.25, 2.13, 3.38)
])
def test_decimal_numbers(self, input1, input2, expected):
assert add(input1, input2) == pytest.approx(expected)
@pytest.mark.parametrize("input1, input2, expected", [
(-1, -1, -2),
(-10, -5, -15),
(-500, -5, -505)
])
def test_negative_numbers(self, input1, input2, expected):
assert add(input1, input2) == expected
@pytest.mark.parametrize("input1, input2, expected", [(-10, 3, -7),(5, -3, 2)])
def test_mixed_numbers(self, input1, input2, expected):
assert add(input1, input2) == expected
@pytest.mark.parametrize("input1, input2", [("1", 2),(1, "2"),("1", "2")])
def test_non_numeric_input(self, input1, input2):
with pytest.raises(TypeError, match="Both input must be number"):
add(input1, input2)
class TestSubtraction:
"""Test cases for subtraction function"""
@pytest.mark.parametrize("input1, input2, expected", [
(5, 2, 3),
(10, 5, 5),
(7, 25, -18)
])
def test_positive_numbers(self, input1, input2, expected):
assert subtract(input1, input2) == expected
@pytest.mark.parametrize("input1, input2, expected", [
(-5, -2, -3),
(-10, -5, -5),
(-7, -25, 18)
])
def test_negative_numbers(self, input1, input2, expected):
assert subtract(input1, input2) == expected
@pytest.mark.parametrize("input1, input2, expected", [
(-5, 2, -7),
(5, -2, 7),
(0, 5, -5)
])
def test_mixed_numbers(self, input1, input2, expected):
assert subtract(input1, input2) == expected
@pytest.mark.parametrize("input1, input2, expected", [
(5.5, 2.2, 3.3),
(10.1, 5.95, 4.15),
(0.0, 0.0, 0.0),
(1.25, 2.13, -0.88)
])
def test_decimal_numbers(self, input1, input2, expected):
assert subtract(input1, input2) == pytest.approx(expected)
@pytest.mark.parametrize("input1, input2", [
("5", 2),
(5, "2"),
("5", "2")
])
def test_non_numeric_input(self, input1, input2):
with pytest.raises(TypeError, match="Both input must be number"):
subtract(input1, input2)
class TestMultiplication:
"""Test cases for multiplication function"""
@pytest.mark.parametrize("input1, input2, expected", [
(2, 3, 6),
(5, 4, 20),
(0, 5, 0)
])
def test_positive_numbers(self, input1, input2, expected):
assert multiply(input1, input2) == expected
@pytest.mark.parametrize("input1, input2, expected", [
(-2, -3, 6),
(-5, -4, 20),
(-2, -4, 8)
])
def test_negative_numbers(self, input1, input2, expected):
assert multiply(input1, input2) == expected
@pytest.mark.parametrize("input1, input2, expected", [
(-2, 3, -6),
(2, -3, -6),
(0, 5, 0)
])
def test_mixed_numbers(self, input1, input2, expected):
assert multiply(input1, input2) == expected
@pytest.mark.parametrize("input1, input2, expected", [
(2.5, 4, 10.0),
(3.1, 2.0, 6.2),
(0.0, 5, 0.0)
])
def test_decimal_numbers(self, input1, input2, expected):
assert multiply(input1, input2) == pytest.approx(expected)
@pytest.mark.parametrize("input1, input2", [
("2", 3),
(2, "3"),
("2", "3")
])
def test_non_numeric_input(self, input1, input2):
with pytest.raises(TypeError, match="Both input must be number"):
multiply(input1, input2)
class TestDivision:
"""Test cases for division function"""
@pytest.mark.parametrize("num, den, expected", [(6, 2, 3),(5, 2, 2.5),(0, 5, 0)])
def test_positive_numbers(self, num, den, expected):
assert divide(num, den) == expected
@pytest.mark.parametrize("num, den, expected", [(-6, -2, 3),(-5, -2, 2.5)])
def test_negative_numbers(self, num, den, expected):
assert divide(num, den) == expected
@pytest.mark.parametrize("num, den, expected", [(-6, 2, -3), (6, -2, -3), (0, -5, 0)])
def test_mixed_numbers(self, num, den, expected):
assert divide(num, den) == expected
@pytest.mark.parametrize("num, den, expected", [(5.5, 2.2, 2.5), (10.1, 5.05, 2.0), (0.0, 1.0, 0.0)])
def test_decimal_numbers(self, num, den, expected):
assert divide(num, den) == pytest.approx(expected)
@pytest.mark.parametrize("num", [-1, 0, 5])
def test_by_zero(self, num):
with pytest.raises(ZeroDivisionError, match="Denominator cannot be zero"):
divide(num, 0)
@pytest.mark.parametrize("num, den", [("6", 2), (6, "2"), ("6", "2")])
def test_non_numeric_input(self, num, den):
with pytest.raises(TypeError, match="Both input must be number"):
divide(num, den)
pytest.mark.parametrize
จะทำงานในระดับ test function ซึ่งช่วยให้เราสามารถกำหนดชุดข้อมูลที่ต้องใช้สำหรับการทดสอบได้หลายชุด ทำให้การทดสอบครอบคลุมกรณีต่างๆ ได้มากขึ้น โดยที่ pytest จะสร้าง test cases แยกกันสำหรับแต่ละชุดข้อมูล
4. การใช้ Markers เพื่อจัดกลุ่มเทส
Markers ช่วยให้เราจัดกลุ่มเทสและรันเฉพาะกลุ่มที่ต้องการได้
เราจะใช้ markers เพื่อจัดกลุ่มเทสตามประเภท positive_numbers
, negative_numbers
, mixed_numbers
, decimal_numbers
, และ non_numeric_input
class TestAddition:
"""Test cases for addition function"""
@pytest.mark.positive_numbers
@pytest.mark.parametrize("input1, input2, expected", [(1, 2, 3),(10, 5, 15),(0, 0, 0)])
def test_positive_numbers(self, input1, input2, expected):
assert add(input1, input2) == expected
@pytest.mark.decimal_numbers
@pytest.mark.parametrize("input1, input2, expected", [
(1.1, 2.5, 3.6),
(10.1, 5.95, 16.05),
(0.0, 0.0, 0.0),
(1.25, 2.13, 3.38)
])
def test_decimal_numbers(self, input1, input2, expected):
assert add(input1, input2) == pytest.approx(expected)
@pytest.mark.negative_numbers
@pytest.mark.parametrize("input1, input2, expected", [
(-1, -1, -2),
(-10, -5, -15),
(-500, -5, -505)
])
def test_negative_numbers(self, input1, input2, expected):
assert add(input1, input2) == expected
@pytest.mark.mixed_numbers
@pytest.mark.parametrize("input1, input2, expected", [(-10, 3, -7),(5, -3, 2)])
def test_mixed_numbers(self, input1, input2, expected):
assert add(input1, input2) == expected
@pytest.mark.non_numeric_input
@pytest.mark.parametrize("input1, input2", [("1", 2),(1, "2"),("1", "2")])
def test_non_numeric_input(self, input1, input2):
with pytest.raises(TypeError, match="Both input must be number"):
add(input1, input2)
สำหรับเทสอื่นๆ ก็ทำในลักษณะเดียวกัน โดยเพิ่ม markers ตามกลุ่มที่ต้องการ
Config pytest ด้วยไฟล์ pytest.ini
สร้างไฟล์ pytest.ini
เพื่อกำหนดค่าต่างๆ:
[pytest]
markers =
positive_numbers: Tests with positive numbers
negative_numbers: Tests with negative numbers
mixed_numbers: Tests with mixed positive and negative numbers
decimal_numbers: Tests with decimal numbers
non_numeric_input: Tests for non-numeric input
addopts = -v
รันเทสตาม Marker
# รันเฉพาะ เทสที่เป็น positive_numbers
pytest -m positive_numbers
# รันเฉพาะเทส ที่เป็น decimal_numbers
pytest -m decimal_numbers
# รันทุกเทสยกเว้นที่ non_numeric_input
pytest -m "not non_numeric_input"
# รันเทสที่เป็น positive_numbers หรือ negative_numbers
pytest -m "positive_numbers or negative_numbers"
# จะไม่มีเทสที่ถูกรันเลย เพราะไม่มีเทสไหนที่ระบุ marker positive_numbers และ negative_numbers พร้อมกัน
pytest -m "positive_numbers and negative_numbers"
ทดลองรันเฉพาะ เทสที่เป็น positive_numbers

เทสที่กำหนด marker positive_numbers ของทุกคลาสจะถูกรัน
source code
ยอดเยี่ยม! ตอนนี้เราได้เรียนรู้:
- Assertions ที่หลากหลาย: ไม่ใช่แค่
==
แต่มีin
,isinstance
,raises
และอื่นๆ - การจัดระเบียบเทส: ใช้คลาส, parametrize, และ markers เพื่อทำให้โค้ดดูเรียบร้อยและเข้าใจง่าย
- Configuration: ใช้ pytest.ini เพื่อตั้งค่าต่างๆ ของ pytest สำหรับโปรเจคของเราเช่น กำหนด markers
ในตอนหน้า เราจะไปลึกกับ Fixtures ที่จะทำให้เทสของเราทรงพลังและจัดการกับ dependencies ซับซ้อนได้อย่างมืออาชีพ!
รอติดตาม Part 3:Fixtures ตัวช่วยสุดล้ำ เตรียมพร้อม Test ได้ดั่งใจ! กันได้เลย! 🚀
Leave a Reply