บทที่ 16 Testing & CI/CD
ทำไมต้อง Mock?
ปัญหาหลักของการเขียนโปรแกรมกับ ETABS API คือ:
- Dependency: ต้องมี ETABS ติดตั้งอยู่บนเครื่องที่รัน Test
- Speed: การเปิด/ปิด ETABS กินเวลานานมาก
- Cost: เปลือง License ถ้าต้องรัน Test บ่อยๆ
- CI/CD: บน GitHub Actions หรือ Cloud Server ไม่มี ETABS ให้ใช้
ทางออกคือการทำ Mocking (จำลองพฤติกรรมของ API)
1. รู้จักกับ unittest.mock
Python มี library มาตรฐานสำหรับการทำ Mocking อยู่แล้ว:
from unittest.mock import MagicMock
# สร้าง Fake ETABS Objectmock_etabs = MagicMock()mock_model = mock_etabs.SapModel
# กำหนดค่า Return ล่วงหน้าmock_model.GetModelFilename.return_value = "C:\\Models\\Test.edb"mock_model.FrameObj.GetNameList.return_value = (0, 2, ["C1", "B1"])
# ลองเรียกใช้งานprint(mock_model.GetModelFilename())# Output: C:\Models\Test.edb2. เขียน Test Case จริงด้วย pytest
สมมติเรามีฟังก์ชันที่ต้องการทดสอบ:
def get_column_count(sap_model): """นับจำนวนเสาในโมเดล""" ret, number, names = sap_model.FrameObj.GetNameList() count = 0 for name in names: if name.startswith("C"): count += 1 return countเขียน Test Case โดย Inject Mock Object แทนของจริง:
import pytestfrom unittest.mock import MagicMockfrom src.main import get_column_count
def test_get_column_count(): # 1. Setup Mock mock_model = MagicMock() # จำลองว่าในโมเดลมี 3 frame: C1, B1, C2 mock_model.FrameObj.GetNameList.return_value = (0, 3, ["C1", "B1", "C2"])
# 2. Call Function with Mock result = get_column_count(mock_model)
# 3. Assert assert result == 2 # ต้องนับได้ 2 (C1, C2)
# 4. Verify Call (เช็คว่า code เราเรียก API จริงไหม) mock_model.FrameObj.GetNameList.assert_called_once()
# Run: pytest tests/3. สร้าง Fake ETABS Fixture
เพื่อให้เขียน Test ง่ายขึ้น ควรสร้าง Fixture กลางไว้ใช้ร่วมกัน:
import pytestfrom unittest.mock import MagicMock
@pytest.fixturedef mock_sap_model(): model = MagicMock()
# Default behaviors model.GetModelFilename.return_value = "MockModel.edb" model.SetPresentUnits.return_value = 0
return model4. Continuous Integration (CI/CD)
เมื่อเรา Mock ทุกอย่างแล้ว เราสามารถรัน Test บน GitHub Actions ได้เลย! (แม้ Server จะเป็น Linux และไม่มี ETABS)
.github/workflows/python-test.yml
name: Python Tests
on: [push, pull_request]
jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v4 with: python-version: "3.11" - name: Install dependencies run: | pip install pytest pandas comtypes - name: Run tests run: | pytest tests/