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

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

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

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

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

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

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

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

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

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

มาตรการหลักที่คุณสามารถใช้เพื่อลดความไม่สม่ำเสมอมีดังต่อไปนี้

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

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

เฟรมเวิร์กอื่นๆ เช่น UI Automator ช่วยให้มีขอบเขตการใช้งานที่ใหญ่ขึ้น เนื่องจากคุณโต้ตอบกับ 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