ความเสถียรของการทดสอบแบบกลุ่มใหญ่

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

เฟรมเวิร์กสมัยใหม่อย่าง Compose หรือ Espresso ได้รับการออกแบบมาโดยคำนึงถึงการทดสอบ ดังนั้นจึงมีการรับประกันว่า UI จะหยุดทำงานก่อนการดําเนินการทดสอบหรือการยืนยันครั้งถัดไป การดำเนินการนี้เรียกว่าการซิงค์

การซิงค์การทดสอบ

ปัญหาอาจยังคงเกิดขึ้นเมื่อคุณเรียกใช้การดำเนินการแบบไม่พร้อมกันหรือการดำเนินการเบื้องหลังที่การทดสอบไม่รู้ เช่น การโหลดข้อมูลจากฐานข้อมูลหรือการแสดงภาพเคลื่อนไหวแบบวนซ้ำ

แผนภาพแสดงลูปที่ตรวจสอบว่าแอปไม่มีการใช้งานหรือไม่ก่อนที่จะทำการทดสอบ
รูปที่ 1: ทดสอบการซิงค์

หากต้องการเพิ่มความน่าเชื่อถือของชุดทดสอบ คุณสามารถติดตั้งวิธีติดตามการดำเนินการในเบื้องหลัง เช่น Espresso Idling Resources นอกจากนี้ คุณยังแทนที่โมดูลสําหรับเวอร์ชันการทดสอบที่คุณสามารถค้นหาสถานะไม่มีการใช้งานหรือปรับปรุงการซิงค์ได้ เช่น TestDispatcher สําหรับ Coroutine หรือ RxIdler สําหรับ RxJava

แผนภาพที่แสดงการทดสอบที่ไม่สําเร็จเมื่อการซิงค์อิงตามการรอเวลาคงที่
รูปที่ 2: การใช้ sleep ในการทดสอบทําให้การทดสอบช้าหรือไม่เสถียร

วิธีปรับปรุงความเสถียร

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

มาตรการหลักที่คุณทำได้เพื่อลดความผิดพลาดมีดังนี้

  • กำหนดค่าอุปกรณ์อย่างถูกต้อง
  • ป้องกันปัญหาการซิงค์
  • ใช้งานการลองอีกครั้ง

หากต้องการสร้างการทดสอบขนาดใหญ่โดยใช้ Compose หรือ Espresso โดยทั่วไปคุณจะต้องเริ่มกิจกรรมใดกิจกรรมหนึ่งและไปยังส่วนต่างๆ ตามที่ผู้ใช้ทำ ตรวจสอบว่า UI ทํางานอย่างถูกต้องโดยใช้การยืนยันหรือการทดสอบภาพหน้าจอ

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

กำหนดค่าอุปกรณ์

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

ผู้ให้บริการฟาร์มอุปกรณ์จะกำหนดค่าอุปกรณ์และโปรแกรมจำลอง ดังนั้นคุณจึงไม่จำเป็นต้องดำเนินการใดๆ อย่างไรก็ตาม อาจมีคำสั่งการกำหนดค่าของตนเองสำหรับกรณีพิเศษ

อุปกรณ์ที่มีการจัดการโดย Gradle

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

android {
  testOptions {
    managedDevices {
      localDevices {
        create("pixel2api30") {
          // Use device profiles you typically see in Android Studio.
          device = "Pixel 2"
          // Use only API levels 27 and higher.
          apiLevel = 30
          // To include Google services, use "google".
          systemImageSource = "aosp"
        }
      }
    }
  }
}

เมื่อใช้การกำหนดค่านี้ คำสั่งต่อไปนี้จะสร้างอิมเมจโปรแกรมจำลอง เริ่มอินสแตนซ์ เรียกใช้การทดสอบ และปิดอินสแตนซ์

./gradlew pixel2api30DebugAndroidTest

อุปกรณ์ที่จัดการโดย Gradle มีกลไกในการลองอีกครั้งในกรณีที่อุปกรณ์ตัดการเชื่อมต่อและมีการปรับปรุงอื่นๆ

ป้องกันปัญหาการซิงค์

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

โซลูชัน

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

คุณสามารถทําให้การทดสอบรอจนกว่าจะมีเงื่อนไขที่เฉพาะเจาะจงตรงตามที่ต้องการแทนที่จะประมาณว่ากิจกรรมมีการใช้งานหรือไม่ เช่น คุณสามารถรอจนกว่าข้อความหรือคอมโพเนนต์ที่เฉพาะเจาะจงจะแสดงใน UI

Compose มีคอลเล็กชัน API การทดสอบเป็นส่วนหนึ่งของ ComposeTestRule เพื่อรอการจับคู่ที่แตกต่างกัน ดังนี้

fun waitUntilAtLeastOneExists(matcher: SemanticsMatcher, timeout: Long = 1000L)

fun waitUntilDoesNotExist(matcher: SemanticsMatcher, timeout: Long = 1000L)

fun waitUntilExactlyOneExists(matcher: SemanticsMatcher,  timeout: Long = 1000L)

fun waitUntilNodeCount(matcher: SemanticsMatcher, count: Int, timeout: Long = 1000L)

และ API ทั่วไปที่ใช้ฟังก์ชันใดก็ได้ที่แสดงผลบูลีน

fun waitUntil(timeoutMillis: Long, condition: () -> Boolean): Unit

ตัวอย่างการใช้งาน

composeTestRule.waitUntilExactlyOneExists(hasText("Continue")</code>)</p></td>

กลไกการลองใหม่

คุณควรแก้ไขการทดสอบที่ไม่เสถียร แต่บางครั้งเงื่อนไขที่ทำให้การทดสอบไม่ผ่านนั้นเกิดขึ้นได้ยากมากจนทำให้เกิดปัญหาในการจำลอง แม้ว่าคุณควรติดตามและแก้ไขการทดสอบที่ไม่เสถียรอยู่เสมอ แต่กลไกการลองใหม่จะช่วยรักษาประสิทธิภาพการทำงานของนักพัฒนาแอปไว้ได้ด้วยการเรียกใช้การทดสอบหลายครั้งจนกว่าจะผ่าน

การลองอีกครั้งต้องเกิดขึ้นหลายระดับเพื่อป้องกันปัญหา เช่น

  • การเชื่อมต่อกับอุปกรณ์หมดเวลาหรือขาดการเชื่อมต่อ
  • การทดสอบครั้งเดียวไม่สำเร็จ

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

  • กฎ JUnit ที่พยายามทดสอบซ้ำหลายครั้ง
  • การดำเนินการหรือขั้นตอนที่ลองใหม่ในเวิร์กโฟลว์ CI
  • ระบบสำหรับรีสตาร์ทโปรแกรมจำลองเมื่อไม่ตอบสนอง เช่น อุปกรณ์ที่จัดการโดย Gradle