เมื่อนักพัฒนาแอนดรอยด์ได้พัฒนาแอปไปจนถึงจุดหนึ่ง ก็อาจจะต้องการเขียนเทสเพิ่มเข้าไปในโปรเจคเพื่อช่วยให้นักพัฒนามั่นใจได้ว่าโค้ดที่มีอยู่สามารถทำงานได้ถูกต้องจริง ๆ และเมื่อมีการเพิ่มโค้ดเข้าไปในอนาคตก็รู้ได้ว่าจะไปกระทบการทำงานของโค้ดเก่าตรงไหนบ้าง

โดยปกติแล้วการสร้างโปรเจคแอนดรอยด์จาก Android Studio จะมีโค้ดบางส่วนที่ถูกเตรียมไว้ให้ตั้งแต่แรกเพื่อให้พร้อมสำหรับการเขียนเทสอยู่แล้ว และบนแอนดรอยด์นั้นออกแบบมาให้สามารถเขียนเทสได้หลายระดับ

และเมื่ออ้างอิงจาก Testing Pyramid บนแอนดรอยด์สามารถแบ่งระดับในการเขียนเทสตามของ Testing Pyramid ได้ดังนี้

  • Small Test
  • Medium Test
  • Large Test

Small Test

เป็นการเขียนเทสเพื่อทดสอบการทำงานของโค้ดในคลาสใดคลาสหนึ่ง หรือที่เรียกกันว่า Unit Test นั่นเอง เพื่อให้มั่นใจได้ว่าคำสั่งสามารถทำงานได้ถูกต้องตามหน้าที่ของคลาส

Medium Test

เป็นการเขียนเทสเพื่อทดสอบการทำงานของโค้ดหลายส่วนที่ต้องทำงานร่วมกัน หรือที่เรียกกันว่า Integration Test เพื่อให้มั่นใจได้ว่าโค้ดที่อยู่คนละคลาสที่ต้องทำงานร่วมกัน สามารถทำงานได้อย่างถูกต้องหรือไม่ เช่น

  • การทำงานร่วมกันระหว่าง Fragment กับ ViewModel
  • การทำงานร่วมกันระหว่าง Logic ใน Data-binding กับ ViewModel
  • การทำงานร่วมกันระหว่าง Data Source ที่อยู่ใน Repository Layer

Large Test

เป็นการเขียนเทสเพื่อทดสอบการทำงานของโค้ดทั้งหมด โดยจำลองสถานการณ์ต่าง ๆ ให้เหมือนกับการใช้งานจริงของผู้ใช้ หรือที่เรียกกันว่า End-to-end Test หรือที่เรียกกันว่า UI Test เพื่อให้มั่นใจได้ว่าโค้ดทั้งหมดสามารถทำงานร่วมกันได้ถูกต้องตาม User Journey ที่ออกแบบไว้

แน่นอนว่าโดยพื้นฐานของการเขียนเทสแล้ว นักพัฒนาควรเน้นไปที่การเขียน Small Test เป็นหลัก จากนั้นก็ลดหลั่นกันไปตามลำดับของ Testing Pyramid (ที่แนะนำกันคือ 70-20-10)

Test Execution Environment

ในการเขียนเทสสำหรับโปรเจคของแอนดรอยด์นั้นสามารถแบ่ง Environment ในการรันเทสได้อยู่ 2 แบบด้วยกัน

  • Android Runtime - การเทสที่ทำงานอยู่บนอุปกรณ์แอนดรอยด์ไม่ว่าจะเป็น Physical Device หรือ Android Emulator ก็ตาม
  • Java Virtual Machine (JVM) - การเทสที่ทำงานอยู่บน JVM บนเครื่องคอมที่ใช้รันเทส (Local Machine) นั่นเอง

ซึ่งโค้ดบางส่วนใน Framework API ของแอนดรอยด์จะต้องใช้ Android Runtime โดยเฉพาะโค้ดที่มีการเรียกใช้คลาส Context (แน่นอนว่าโค้ดบางอย่างก็สามารถทำงานบน JVM ได้เช่นกัน) โดยที่การรันเทสบน JVM จะเร็วกว่า Android Runtime เพราะสามารถทำงานบนคอมที่ใช้รันเทสได้ทันทีโดยไม่ต้องใช้อุปกรณ์แอนดรอยด์

และโปรเจคแอนดรอยด์ใน Android Studio ก็จะมีการแยก Build Variant สำหรับ Test Execution Environment ทั้ง 2 แบบให้อยู่แล้ว

  • Android Runtime จะมีชื่อ Variant เป็น androidTest
  • JVM จะมีชื่อ Variant เป็น test

Robolectric

ถึงแม้ว่าโค้ดที่จำเป็นต้องทำงานอยู่บน Android Runtime แต่ในปัจจุบันก็มีตัวช่วยอย่าง Robolectric ที่สามารถจำลอง Android Runtime ให้ทำงานอยู่บน JVM ได้ ทำให้ไม่ต้องต่อกับอุปกรณ์แอนดรอยด์จริง ๆ เพื่อรันเทสผ่าน Android Runtime (วิธีนี้จะใช้เวลานานกว่า JVM แต่ก็ใช้เวลาน้อยกว่าบน Android Runtime โดยตรง)

โดย Robolectric จะมีอยู่ใน AndroidX Test อยู่แล้วจึงสามารถใช้งานได้เลย

เขียนเทสแบบไหนต้องใช้ Environment ไหน?

การเลือก Environment จะไม่ได้ยึดติดว่าต้องใช้กับการเขียนเทสแบบไหน แต่ขึ้นอยู่กับนักพัฒนาว่าการเทสแต่ละอย่างนั้นต้องการใช้ Environment แบบไหน แต่โดยปกติแล้วการเทสส่วนใหญ่จะพยายามใช้ JVM เป็นหลัก เพราะใช้เวลาในการรันเทสน้อยกว่า ดังนั้น

  • Unit Test - พยายามใช้ JVM ให้เยอะที่สุดเท่าที่ทำได้ เพราะเป็นเทสที่มีเยอะที่สุดในโปรเจค และถ้าเทสอันไหนที่จำเป็นต้องใช้ Android Runtime ให้ใช้เป็น Robolectric เพื่อให้ทำงานบน JVM แทน
  • Integration Test - พยายามใช้ JVM ให้เยอะที่สุดเหมือนกับ Unit Test แต่ในบางกรณีของ Integration Test ก็จำเป็นต้องใช้ Android Runtime
  • E2E Test หรือ UI Test - จำเป็นต้องใช้ Android Runtime เพราะเป็นการเทสที่ต้องทดสอบบนอุปกรณ์แอนดรอยด์

Framework / Library ที่จำเป็นต่อการเขียนเทสบนแอนดรอยด์

AndroidX Test

Library จาก Android Jetpack เพื่อช่วยให้การเขียนเทสบนแอนดรอยด์สามารถทำได้ง่ายขึ้น เพราะจะมีคำสั่งต่าง ๆ ที่ช่วยสำหรับการเขียนเทส

JUnit

Unit Test Framework สำหรับ Java และ Kotlin

Robolectric

อันนี้ เจ้าของบล็อกได้บอกไปแล้วว่าเป็น Library ที่ช่วยจำลอง Android Runtime บน JVM เพื่อช่วยให้เทสบางตัวสามารถใช้ JVM แทนเพื่อลดระยะเวลาในการรันเทสให้น้อยลง

Espresso

Library จาก Android Jetpack เพื่อช่วยเขียน UI Test บนแอนดรอยด์

เพราะโดยปกติแล้วการทำ UI Test จะเป็นการเทสที่ไม่เข้าไปยุ่งกับการทำงานของโค้ดโดยตรง จึงต้องมี Library อย่าง Espresso ที่ช่วยให้นักพัฒนาสามารถเขียนเพื่อจำลองการใช้งานของผู้ใช้ได้ เช่น กดปุ่ม, พิมพ์ข้อความ, เลื่อนหน้าจอ เป็นต้น รวมไปถึงการเช็ค UI บนหน้าจอว่าแสดงผลถูกต้องตามที่กำหนดหรือไม่

แหล่งข้อมูลอ้างอิง