แชร์เรื่องนี้
เคล็ดลับจัดการ Snackbar ใน Jetpack Compose เพื่อให้ทดสอบ UI อย่างแม่นยำและเชื่อถือได้
โดย Seven Peaks เมื่อ 11 ก.พ. 2026, 10:31:35
ในการพัฒนาแอปฯ Android ยุคใหม่ ทีมพัฒนาต่างหันมาพึ่งพาการทดสอบ UI แบบอัตโนมัติมากขึ้นเรื่อยๆ ตามความซับซ้อนของแอปฯ ที่เพิ่มขึ้น อย่างไรก็ตาม มีส่วนหน้าจอผู้ใช้ (UI) บางตัวที่ดูเหมือนจะสร้างง่าย แต่กลับทดสอบได้ยากอย่างน่าประหลาด คุณสมเกียรติ กิจวงศ์วัฒนะ Staff Software Engineer จาก LINE MAN Wongnai ได้มาแบ่งปันข้อมูลเชิงลึกเกี่ยวกับตัวการสำคัญที่มักทำให้การทดสอบรวนอยู่บ่อยครั้ง นั่นก็คือ Snackbar นั่นเอง
ทำไมการทดสอบ Snackbar แบบมาตรฐานถึงมักจะล้มเหลว
วิธีการเขียน Snackbar ทั่วไปใน Jetpack Compose มักจะใช้ Scaffold, SnackbarHost และ SnackbarHostState ซึ่งวิธีนี้ทำงานได้สมบูรณ์แบบสำหรับผู้ใช้งานทั่วไป แต่พอนำไปรันในสภาพแวดล้อมระบบอัตโนมัติอย่าง CI/CD มันมักจะเกิดปัญหาเรื่อง Timing ตามมา
คุณลองนึกถึงภาพขั้นตอนการเก็บคูปองดู
- การกระทำ: ผู้ใช้กดปุ่ม "เก็บคูปอง"
- Snackbar แรก: แอปฯ แสดงข้อความ "กำลังใช้คูปอง..."
- ปัญหาเรื่องจังหวะ (Race): หากเซิร์ฟเวอร์ตอบกลับเร็วมาก แอปฯ จะพยายามแสดง Snackbar ตัวที่สอง ("เก็บคูปองสำเร็จ!") ก่อนที่ตัวแรกจะหายไป
ในการทดสอบ UI การตรวจสอบข้อความที่สองมักจะล้มเหลว เพราะข้อความแรกยังไม่ทันหายไปจากหน้าจอเลย นักพัฒนาหลายคนจึงมักเลือกที่จะตัดการตรวจสอบทิ้งเพียงเพื่อให้การทดสอบผ่านไปได้ แต่นั่นหมายถึงการยอมลดคุณภาพของงานลง และส่งผลต่อประสบการณ์ของผู้ใช้อย่างหลีกเลี่ยงไม่ได้
การสร้าง Controller และ Container ที่เป็นมิตรต่อการทดสอบ
เพื่อขจัดปัญหาการทดสอบที่รวนจนทำงานต่อได้ลำบาก เราจึงควรเปลี่ยนมาใช้โครงสร้างแบบสองส่วนที่ช่วยให้เราควบคุมพฤติกรรมของ Snackbar ในการทดสอบได้ด้วยตัวเอง นั่นคือการใช้ Controller เพื่อสั่งปิดข้อความ และ Container เพื่อจัดการระยะเวลาการแสดงผล
1. SnackbarUiTestController
-
วัตถุประสงค์: ทำหน้าที่เป็นตัวลงทะเบียนเพื่อเก็บอินสแตนซ์ของ SnackbarHostState ที่ถูกสร้างขึ้นตอนแอปฯ ทำงาน เพื่อให้เราสามารถสั่งปิดข้อความ (Dismiss) ผ่านโค้ดการทดสอบได้โดยตรง
-
ฟังก์ชันหลัก:
-
registerSnackbarHostState: เพิ่ม State เข้าไปในระบบติดตาม
-
dismissCurrentSnackbar: สั่ง dismiss() Snackbar ที่กำลังแสดงอยู่ด้วยตนเอง
-
clearAllSnackbarHostStates: เคลียร์ข้อมูลหลังจบการทดสอบเพื่อป้องกันปัญหาหน่วยความจำรั่ว
-
2. SnackbarHostStateProvider
เพื่อให้โค้ดที่ใช้งานจริง (Production code) ยังคงสะอาดอยู่ Provider ตัวนี้จะใช้ Java Reflection เพื่อเช็กว่ามี Test Controller พร้อมใช้งานหรือไม่
-
หากแอปฯ รันอยู่ในสภาพแวดล้อมการทดสอบ มันจะลงทะเบียน SnackbarHostState กับ Controller โดยอัตโนมัติ
-
ใช้ CompositionLocalProvider เพื่อให้ State นี้ถูกเรียกใช้งานได้ง่ายทั่วทั้งโครงสร้าง UI (UI Tree)
3. SnackbarContainer
คอมโพเนนต์ตัวนี้จะแก้ปัญหาเรื่องระยะเวลาการแสดงผล ในการใช้งานจริง Snackbar จะมีระยะเวลาแบบสั้นหรือยาว แต่เมื่อฟังก์ชัน isRunningUiTest() เป็นจริง Container จะเปลี่ยนระยะเวลาให้เป็นแบบ Indefinite (แสดงค้างไว้ตลอด) วิธีนี้ช่วยให้มั่นใจว่า Snackbar จะอยู่บนหน้าจอนานพอที่ตัวทดสอบจะตรวจสอบความถูกต้องได้ และไม่หายไปก่อนเวลาที่มันควรจะอยู่ถึง
การนำขั้นตอนที่เอื้อต่อการทดสอบไปใช้งาน
ด้วยโครงสร้างแบบนี้ การทดสอบ UI ของคุณจะคาดเดาผลได้และอ่านง่ายขึ้นมาก แทนที่จะต้องมานั่งลุ้นเรื่องจังหวะเวลา โค้ดการทดสอบของคุณจะทำงานตามรูปแบบที่ชัดเจนคือ "ตรวจสอบ แล้วจึงสั่งปิด" (Verify-and-dismiss pattern)
Kotlin
@Test |
ข้อควรระวังสำหรับการนำไปใช้จริง
แม้ว่าวิธีนี้จะมีประสิทธิภาพมาก แต่มี 2 จุดสำคัญที่ต้องระลึกไว้เสมอ
1. อย่าให้โค้ดทดสอบหลุดไปในงานจริง: ตรวจสอบให้แน่ใจว่า SnackbarUiTestController อยู่ในโฟลเดอร์ androidTest เท่านั้น หากหลุดไปอยู่ใน Production ตัว Snackbar ของคุณอาจจะค้างอยู่บนหน้าจอผู้ใช้จริงตลอดกาลได้
2. ความซับซ้อน: ในแอปฯ ที่ซับซ้อน Snackbar จากคอมโพเนนต์อื่นอาจจะโผล่ขึ้นมาแทรกระหว่างการทดสอบได้ ดังนั้นควรใช้เมธอด @After เพื่อเคลียร์ Host States ทั้งหมดระหว่างจบแต่ละการทดสอบเสมอ
เน้นความเสถียร
มากกว่าความเรียบง่าย
การบังคับให้ Snackbar แสดงค้างไว้ (Indefinite duration) ระหว่างการทดสอบ และการใช้ Controller ส่วนกลางเพื่อสั่งปิดข้อความ จะช่วยเปลี่ยนการทดสอบ Snackbar ที่เคยรวนให้กลายเป็นส่วนที่เสถียรที่สุดในชุดทดสอบของคุณ วิธีนี้ก้าวข้ามจากการหวังว่า UI จะอยู่นิ่งๆ ไม่เกิดปัญหา ไปเป็นการมอบเครื่องมือให้นักพัฒนาสั่งการมันได้ดั่งใจ
คุณสมเกียรติ กิจวงศ์วัฒนะ
Staff Software Engineer - Android at LINE MAN Wongnai
คุณสมเกียรติเป็นนักพัฒนา Android ที่มีความหลงใหลในสายงานนี้มานานกว่า 10 ปี มีประสบการณ์ทั้งในด้านการพัฒนาซอฟต์แวร์ระดับองค์กร และเป็นผู้ที่มีส่วนร่วมขับเคลื่อนชุมชน Android ในประเทศไทยอย่างต่อเนื่อง ก่อนหน้านี้เขาเคยทำงานเป็นนักพัฒนาฮาร์ดแวร์ สร้างระบบฝังตัว (Embedded Systems) ที่ทำงานเชื่อมต่อกับอุปกรณ์ Android
พร้อมจะมาลองดูกันไหมว่า การพัฒนาแบบ AI-native จะช่วยให้โปรเจกต์ถัดไปของคุณสำเร็จได้เร็วขึ้นยังไง? ติดต่อเราได้เลย
แชร์เรื่องนี้
- Product Development (88)
- Service Design (56)
- Industry Insights (48)
- Data Analytics (45)
- AI Innovation (42)
- Product Design (35)
- Product Growth (27)
- Career (25)
- Product Discovery (25)
- Cloud Services (24)
- Quality Assurance (23)
- Events (19)
- CSR (5)
- PR (5)
- Intelligent App (2)
- AI (1)
- Data (1)
- Data Center (1)
- Digital Product (1)
- Oil & Gas (1)
- UX Design (1)
- consumer tech (1)
- กุมภาพันธ์ 2026 (10)
- มกราคม 2026 (6)
- ธันวาคม 2025 (6)
- พฤศจิกายน 2025 (1)
- ตุลาคม 2025 (6)
- กันยายน 2025 (12)
- สิงหาคม 2025 (6)
- กรกฎาคม 2025 (1)
- มิถุนายน 2025 (3)
- มีนาคม 2025 (3)
- กุมภาพันธ์ 2025 (7)
- พฤศจิกายน 2024 (1)
- สิงหาคม 2024 (1)
- กรกฎาคม 2024 (2)
- มีนาคม 2024 (5)
- กุมภาพันธ์ 2024 (5)
- มกราคม 2024 (14)
- ธันวาคม 2023 (4)
- พฤศจิกายน 2023 (9)
- ตุลาคม 2023 (13)
- กันยายน 2023 (7)
- กรกฎาคม 2023 (4)
- มิถุนายน 2023 (3)
- พฤษภาคม 2023 (3)
- เมษายน 2023 (1)
- มีนาคม 2023 (1)
- พฤศจิกายน 2022 (1)
- สิงหาคม 2022 (4)
- กรกฎาคม 2022 (1)
- มิถุนายน 2022 (3)
- เมษายน 2022 (6)
- มีนาคม 2022 (3)
- กุมภาพันธ์ 2022 (6)
- มกราคม 2022 (3)
- ธันวาคม 2021 (2)
- ตุลาคม 2021 (1)
- กันยายน 2021 (1)
- สิงหาคม 2021 (3)
- กรกฎาคม 2021 (1)
- มิถุนายน 2021 (2)
- พฤษภาคม 2021 (1)
- มีนาคม 2021 (4)
- กุมภาพันธ์ 2021 (4)
- ธันวาคม 2020 (3)
- พฤศจิกายน 2020 (1)
- มิถุนายน 2020 (1)
- เมษายน 2020 (1)