Chapter 13 — End-to-End Integration Testing & Real-World Simulation
ทำไมต้องชื่อนี้และเรียนไปเพื่ออะไร?
ตั้งแต่ Day 1 ถึง Day 12 เราได้สร้างและทดสอบชิ้นส่วนทีละชิ้นอย่างประณีต: ตัวยิง Ollama (Day 1-2), ตัวจัดการ Tool Dispatcher (Day 3), เครื่องมืออ่านไฟล์ (Day 4), เครื่องมือรัน Maven Subprocess (Day 5), วงจร ReAct Loop (Day 6), หน้ากาก CLI (Day 7 & 11), หน่วยความจำ Sliding Window (Day 8), ฐานข้อมูล ChromaDB Vector Search (Day 9), ระบบ Safe Patch Diff (Day 10), และกำแพงป้องกัน Guardrails (Day 12)
แต่คำถามคอขาดบาดตายก่อนนำไปใช้จริงคือ: "เมื่อชิ้นส่วนทั้งหมดต้องมาทำงานร้อยเรียงพร้อมกันในโปรเจกต์ที่มีบั๊กแอบแฝงจริงๆ ระบบจะสามารถคุยกันรู้เรื่องตั้งแต่ต้นจนจบ (End-to-End) หรือไม่?"
Day 13 เราจึงต้องสร้าง Test Harness & Simulation Pipeline เพื่อทำการจำลองสถานการณ์จริง (Real-World Bug Simulation):
1. Full Component Pipeline: สั่งงาน 1 คำสั่ง ให้ Agent วิเคราะห์ Error Log ➔ ค้นหาไฟล์ด้วย Vector/Reader ➔ แก้ไขโค้ดด้วย Unified Diff ➔ รันซ้ำจนผ่าน
2. Regression Zero-Check: พิสูจน์ว่าการแก้บั๊กจุดหนึ่ง ไม่ได้แอบไปทำลายส่วนอื่นๆ จนเทสตัวเดิมพัง
3. Automated Test Harness (`test_e2e_simulation.py`): สคริปต์อัตโนมัติที่สั่งรันและ Assert สถานะความสำเร็จระดับ 100% บน Mac M3!
ใน Day 13 เราจะเพิ่มชุดจำลองโปรเจกต์ที่มีหลายไฟล์และหลายเทสใน sample_maven_project/ และสร้างสคริปต์ทดสอบบูรณาการระบบรวม test_e2e_simulation.py เพื่อประเมินผล:
ทำหน้าที่เป็น End-to-End Test Harness ตั้งแต่เริ่มรีเซ็ตสถานะโค้ดที่มีบั๊ก ป้อนคำสั่งทดสอบเข้าสู่ ReActAgentEngine ติดตามการทำงานของโมดูลทั้งหมด (Memory + Dispatcher + Guardrails + Subprocess) และยืนยันว่าการแก้ไขสำเร็จ 100% โดยชุดทดสอบทั้งสองคลาสใน Maven ผ่านฉลุย
ทำหน้าที่เป็น Real-World Bug Simulation Target โดยจำลองบั๊กยอดฮิตระดับองค์กรคือ NullPointerException เมื่อผู้ใช้ไม่ได้ระบุอีเมล ทำให้ Agent ต้องวิเคราะห์ Stack Trace และเพิ่ม Validation Guard Clause ป้องกันข้อผิดพลาด
รันเทสรอบแรกเพื่อจับ Stack Trace
เปิดส่องอ่านบรรทัดที่มีข้อผิดพลาด
ผ่าน Guardrail + Backup + Diff
พิสูจน์ว่าผลลัพธ์ BUILD SUCCESS
สรุปรายงานวิธีแก้ให้มนุษย์ฟัง
1. เราไม่รู้เลยว่าเมื่อ Ollama ได้รับผลเทสยาวๆ ข้อความจะหลุด Format หรือไม่
2. เราไม่รู้ว่า Guardrail จะเผลอไปบล็อกไฟล์ที่ถูกต้องเพราะพาทเพี้ยนหรือไม่
3. เสี่ยงที่ Agent จะแก้ไฟล์แรกผ่าน แต่เผลอทำให้อีกไฟล์พัง (Regression Error) โดยที่เราไม่รู้ตัว
1. พิสูจน์ว่าหน่วยความจำ Sliding Window, Guardrails และ Dispatcher เชื่อมต่อกัน 100% ไร้รอยต่อ
2. พิสูจน์ว่า AI สามารถแกะ Stack Trace จริง เจาะจงอ่านไฟล์ และส่ง Diff ที่แม่นยำ
3. ยืนยันว่าชุดทดสอบทั้งโปรเจกต์ผ่านทุกเคส การันตีความพร้อมในการใช้งานเป็นคู่หูวิศวกรส่วนตัว!
package com.example;
public class UserService {
/**
* แปลงโดเมนอีเมลให้เป็นตัวพิมพ์เล็ก
* [บั๊กจำลอง]: ลืมเช็คกรณี email เป็นค่า null หรือว่างเปล่า
* ทำให้เกิด NullPointerException เมื่อเรียก email.trim()
*/
public String normalizeEmail(String email) {
// บั๊ก: ไม่ได้ตรวจ if (email == null)
return email.trim().toLowerCase();
}
public boolean isValidUser(String username, String email) {
return username != null && !username.isEmpty() && email != null && email.contains("@");
}
}
package com.example;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class UserServiceTest {
@Test
void testNormalizeEmail_Success() {
UserService service = new UserService();
assertEquals("[email protected]", service.normalizeEmail(" [email protected] "));
}
@Test
void testNormalizeEmail_NullSafe() {
UserService service = new UserService();
// คาดหวังว่าเมื่อส่งค่า null เข้าไป จะต้องได้สตริงว่าง "" กลับมาโดยไม่เกิด NullPointerException
assertEquals("", service.normalizeEmail(null));
}
@Test
void testIsValidUser() {
UserService service = new UserService();
assertTrue(service.isValidUser("alice", "[email protected]"));
assertFalse(service.isValidUser("", "[email protected]"));
}
}
import sys
import shutil
from pathlib import Path
from core import (
OllamaLLMClient,
ToolDispatcher,
ReActAgentEngine,
MaxIterationsReachedError
)
from core.tools import read_file, run_maven_test, apply_code_patch, rollback_file
SYSTEM_PROMPT = (
"You are an Autonomous Software Engineering Agent running locally on Apple Silicon Mac.\n"
"You operate in a strict ReAct loop (Reason, Act, Observe).\n"
"You have access to tools to inspect local code, run Maven tests, and safely apply refactoring patches.\n\n"
"Available Tools:\n"
"1. read_file(filepath: string): Reads file content safely from workspace.\n"
"2. run_maven_test(project_dir: string): Runs Maven unit tests for a Java project.\n"
"3. apply_code_patch(filepath: string, new_code: string): Safely applies code patch with automatic backup and diff.\n"
"4. rollback_file(filepath: string): Instantly restores original file from .bak backup.\n\n"
"Response Rules:\n"
"- When taking action, reply strictly with JSON:\n"
"{\n"
' "thought": "Your reasoning why you take this step",\n'
' "tool": "tool_name",\n'
' "arguments": { ... }\n'
"}\n\n"
"- When task is completed, reply strictly with JSON:\n"
"{\n"
' "thought": "Summary of achievements",\n'
' "final_answer": "Comprehensive conclusion and explanation for human developer"\n'
"}\n\n"
"Respond ONLY with valid JSON."
)
def run_simulation():
print("=" * 70)
print("🚀 เริ่มต้น Day 13: E2E Integration Testing & Real-World Simulation")
print("=" * 70)
project_root = Path("./sample_maven_project").resolve()
if not (project_root / "pom.xml").exists():
print(f"❌ ไม่พบโฟลเดอร์โปรเจกต์เป้าหมาย: {project_root}")
sys.exit(1)
# 1. ตรวจสอบการเชื่อมต่อโมเดล
client = OllamaLLMClient(model_name="qwen2.5-coder:7b")
if not client.check_health():
print("❌ ไม่พบ Ollama Daemon หรือโมเดล qwen2.5-coder:7b!")
sys.exit(1)
# 2. ติดตั้ง Dispatcher พร้อมเชื่อมต่อ Tools ครบทั้ง 4 ชิ้น
dispatcher = ToolDispatcher(workspace_root=str(Path.cwd()))
dispatcher.register("read_file", read_file)
dispatcher.register("run_maven_test", run_maven_test)
dispatcher.register("apply_code_patch", apply_code_patch)
dispatcher.register("rollback_file", rollback_file)
# 3. สร้าง ReAct Agent Engine (จำกัดรอบสูงสุด 6 ขั้นตอน)
engine = ReActAgentEngine(client=client, dispatcher=dispatcher, max_iterations=6, memory_window=6)
# 4. โจทย์สถานการณ์จริง: สั่งให้ Agent ตรวจสอบและซ่อมแซมบั๊ก
user_prompt = (
"ในโปรเจกต์ sample_maven_project มีบั๊กทำให้ Unit Test ล้มเหลว "
"ช่วยรันเทสเพื่อหาจุดพัง ตรวจสอบไฟล์โค้ดที่เกี่ยวข้อง แก้ไขให้ถูกต้อง และรันเทสยืนยันจนผ่านทั้งหมด 100%"
)
print(f"\n💬 คำสั่งที่ป้อนให้ Agent:\n\"{user_prompt}\"\n")
print("-" * 70)
try:
# สั่งให้ Agent เริ่มปฏิบัติการอัตโนมัติ
final_answer = engine.run(user_prompt=user_prompt, system_prompt=SYSTEM_PROMPT)
print("\n" + "=" * 70)
print("🎉 ผลการปฏิบัติงานสุดท้ายจาก Agent (Final Report):")
print("=" * 70)
print(final_answer)
# 5. [AUTOMATED VERIFICATION HARNESS]: ยืนยันอิสระด้วยคำสั่ง Maven อีกครั้ง
print("\n" + "=" * 70)
print("🔍 ขั้นตอนตรวจสอบอิสระ (Verification Harness Assertion):")
print("=" * 70)
final_test_output = run_maven_test(project_dir="sample_maven_project")
print(final_test_output)
assert "BUILD SUCCESS" in final_test_output, "❌ การทดสอบไม่ผ่าน! โค้ดยังคงมีบั๊กตกค้าง"
print("\n✅ การันตี 100%: Unit Test ทั้งหมดผ่านฉลุย ปราศจากบั๊กและไม่มี Regression!")
print("🏆 สถาปัตยกรรม Local SE Agent ของคุณผ่านการทดสอบบูรณาการระดับองค์กรเรียบร้อยแล้ว!")
except MaxIterationsReachedError as err:
print(f"\n🛑 Guardrail Alert: {err}")
except Exception as err:
print(f"\n❌ ข้อผิดพลาดในการรัน Simulation: {err}")
if __name__ == "__main__":
run_simulation()
ชำแหละไวยากรณ์สำคัญใน test_e2e_simulation.py
engine = ReActAgentEngine(..., max_iterations=6, memory_window=6)
Bounded Execution & Working Memory: ผูก ReAct Brain เข้ากับ Sliding Window Context เพื่อควบคุมไม่ให้ Token บวมพอง พร้อมตั้งขีดจำกัด 6 รอบ ป้องกันปัญหาลูปค้าง
dispatcher.register("apply_code_patch", apply_code_patch)
Mutation Privilege Delegation: มอบสิทธิ์ให้ Agent สามารถแก้ไขโค้ดจริงในดิสก์ได้ แต่ต้องอยู่ภายใต้การสกัดกั้นของ SafetyGuardrailManager จาก Day 12
assert "BUILD SUCCESS" in final_test_output
Harness Assertion: สคริปต์ทดสอบไม่เชื่อเพียงคำพูดใน final_answer ของ AI แต่สั่งรันคำสั่งระบบปฏิบัติการซ้ำอย่างเป็นกลาง เพื่อพิสูจน์ผลลัพธ์ว่าโค้ดได้รับการแก้ไขจริง
เปิด Terminal บน MacBook M3 แล้วทำตาม 5 ขั้นตอนนี้เพื่อติดตั้งโค้ดจำลองสถานการณ์จริงและสั่งรันการทดสอบบูรณาการทันที:
cd ~/se_agent
source .venv/bin/activate
touch sample_maven_project/src/main/java/UserService.java
touch sample_maven_project/src/test/java/UserServiceTest.java
touch test_e2e_simulation.py
- นำโค้ดใน ส่วนที่ 4 (ไฟล์ที่ 1) ไปวางใน sample_maven_project/src/main/java/UserService.java
- นำโค้ดใน ส่วนที่ 4 (ไฟล์ที่ 2) ไปวางใน sample_maven_project/src/test/java/UserServiceTest.java
- นำโค้ดใน ส่วนที่ 4 (ไฟล์ที่ 3) ไปวางใน test_e2e_simulation.py
python test_e2e_simulation.py
🎉 ผลลัพธ์: คุณจะเห็น Agent วิเคราะห์ลูปอัตโนมัติอย่างชาญฉลาด:
👉 Step 1: สั่ง run_maven_test ➔ ตรวจพบว่า testNormalizeEmail_NullSafe ล้มเหลวด้วย NullPointerException
👉 Step 2: สั่ง read_file อ่าน UserService.java
👉 Step 3: สั่ง apply_code_patch เพิ่ม if (email == null) return "";
👉 Step 4: สั่ง run_maven_test ซ้ำ ➔ ผลลัพธ์ BUILD SUCCESS ผ่าน 100%!
👉 Step 5: ส่ง final_answer สรุปงานเสร็จสมบูรณ์!
python main.py -t "ช่วยรันเทสใน sample_maven_project แล้วสรุปสถานะล่าสุดให้ฟัง"
คุณจะเห็นหน้าจอ Cockpit รายงานผลลัพธ์ในกรอบสีเขียวอย่างสวยงาม ยืนยันว่าทุกชุดทดสอบในโปรเจกต์ผ่านฉลุย!
ข้อที่ 1: วัตถุประสงค์หลักของการทำ "End-to-End Integration Testing & Real-World Simulation" ใน Day 13 คืออะไร?
ข้อที่ 2: ความแตกต่างสำคัญระหว่าง "Unit Testing ของโมดูลเดี่ยว" กับ "E2E Integration Testing ของ Agent" คืออะไร?
ข้อที่ 3: ในสถานการณ์จำลองของ Day 13 บั๊กที่จำลองขึ้นใน `UserService.java` เกิดจากสาเหตุใด?
ข้อที่ 4: เมื่อ Agent เริ่มประมวลผลโจทย์ใน `test_e2e_simulation.py` ขั้นตอนแรกที่มันควรตัดสินใจทำตามหลักวิศวกรรมคืออะไร?
ข้อที่ 5: ปรากฏการณ์ "Regression Bug" หมายถึงอะไร และเราป้องกันใน Day 13 อย่างไร?
ข้อที่ 6: ใน `test_e2e_simulation.py` ทำไมเราจึงต้องใส่คำสั่ง `assert "BUILD SUCCESS" in final_test_output` หลัง Agent ทำงานเสร็จ?
ข้อที่ 7: บทบาทของ `SafetyGuardrailManager` ที่เราสร้างใน Day 12 ช่วยอะไรในระหว่างการรัน E2E Simulation?
ข้อที่ 8: หาก Agent สั่ง Patch โค้ดแล้วทำให้เทสล้มเหลวมากกว่าเดิม สถาปัตยกรรมของเราเตรียมพร้อมรับมืออย่างไร?
ข้อที่ 9: บทบาทของ `SlidingWindowContext` (Day 8) ในระหว่างที่ Agent วนลูปแก้ปัญหา 4-5 ขั้นตอนคืออะไร?
ข้อที่ 10: ค่า `max_iterations = 6` ใน ReAct Engine มีความสำคัญเชิงวิศวกรรมความปลอดภัยอย่างไร?
ข้อที่ 11: การที่ระบบทั้งหมดรันบน MacBook M3 16GB แบบ Local 100% มีข้อได้เปรียบด้านความเร็วในการทำ E2E อย่างไร?
ข้อที่ 12: การออกแบบซอฟต์แวร์แบบ Clean Architecture ที่เราทำมาตลอด ส่งผลดีต่อการทำ Integration Testing อย่างไร?
ข้อที่ 13: เมื่อรัน `test_e2e_simulation.py` สำเร็จแล้ว ใน Day 14 (วันสุดท้ายของ Phase 1.5) เราจะทำสิ่งใด?
ข้อที่ 14: ทำไมการทดสอบ E2E ใน Day 13 ถึงพิสูจน์ว่าคุณไม่จำเป็นต้องจ่ายเงินซื้อ Cloud Copilot อีกต่อไป?
ข้อที่ 15: ในฐานะวิศวกรซอฟต์แวร์ เมื่อเห็นระบบ Agent ที่คุณสร้างกับมือสามารถแก้บั๊กและรันเทสผ่านฉลุยใน Day 13 คุณรู้สึกอย่างไร?
🏁 สรุปหมุดเชื่อมโยง Day 13 (Chapter Wrap-up)
ใน Day 13 — End-to-End Integration Testing & Real-World Simulation คุณได้พิสูจน์แล้วว่า สถาปัตยกรรมทั้งหมดที่คุณค่อยๆ ประกอบขึ้นมาทีละชิ้นตลอด 12 วัน สามารถทำงานประสานกันได้อย่างไร้รอยต่อ: สั่งรันเทสจับ Error ➔ อ่านไฟล์ ➔ สกัดกั้นความปลอดภัยด้วย Guardrail ➔ แก้ไขโค้ดด้วย Unified Diff ➔ และรันเทสซ้ำจนผ่านฉลุย 100% ตอนนี้ระบบของคุณพร้อมสำหรับการใช้งานจริงในชีวิตประจำวันแล้ว พร้อมก้าวเข้าสู่วันสุดท้ายของ Phase 1.5: Day 14 — Final Polish & Personal Developer Workspace เพื่อติดตั้งให้กลายเป็นคำสั่งประจำเครื่องของคุณแล้วครับ!