ถึงแม้จะเป็น Context เหมือนกัน แต่ในบางครั้งก็ใช้แทนกันไม่ได้นะ

ในการเขียนเทสแบบ Instrumented Test ก็อาจจะมีการเรียกใช้งาน Context เป็นบางครั้ง โดยนักพัฒนาสามารถดึง Context ในระหว่าง Instrumented Test ได้จากคลาสที่ชื่อว่า InstrumentationRegistry แบบนี้

val context: Context = InstrumentationRegistry.getInstrumentation().targetContext

ซึ่งเป็นคำสั่งที่คุ้นเคยกันดี เพราะคำสั่งดังกล่าวจะอยู่ในตัวอย่างการเขียนเทสของ Instrumented Test ที่มาพร้อมกับตอนสร้างโปรเจคใหม่บน Android Studio

แต่รู้หรือไม่ว่า จริง ๆ แล้วนักพัฒนาสามารถเรียกใช้งาน Context จาก InstrumentationRegistry ได้ทั้งหมด 2 รูปแบบด้วยกัน

val context: Context = InstrumentationRegistry.getInstrumentation().targetContext
val context: Context = InstrumentationRegistry.getInstrumentation().context

อ้าว ทำไมมีให้ใช้ทั้ง 2 แบบล่ะ? แล้วควรใช้แบบไหนกันแน่?

ถ้าจะให้ตอบแบบสั้น ๆ ก็คือ targetContext เป็น Context ของแอปหลักที่เราพัฒนาขึ้นมา ส่วน context เป็น Context ของแอปที่เกิดมาจาก Instrumented Test

แต่เพื่อให้เข้าใจมากขึ้น...

สิ่งที่เกิดขึ้นเมื่อนักพัฒนาสั่งให้ Instrumented Test ทำงาน

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

  • แอปตัวที่ 1 - แอปที่เราพัฒนาขึ้นมาตามปกติ
  • แอปตัวที่ 2 - แอปที่มีโค้ดสำหรับ Instrumented Test

นั่นจึงเป็นที่มาว่าทำไมคลาส Instrumentation มี targetContext และ context ให้เรียกใช้งานแยกกัน เพราะเป็น Context ของแอปคนละตัวนั่นเอง

และเป็นที่มาของ testApplicationId ใน build.gradle ด้วย เพื่อให้นักพัฒนากำหนด Package Name หรือ Application ID สำหรับแอปที่เป็น Instrumented Test ได้ตามใจชอบ

ตั้งชื่อใหม่ จะได้ไม่สับสน

เนื่องจากทั้งคู่ก็เป็น Context เหมือนกัน ทำให้โค้ดตัวอย่างของ Instrumented Test มีการประกาศชื่อตัวแปรไว้อย่างชัดเจนว่าเป็น Context ของอะไร

เพื่อไม่ให้สับสนระหว่าง Context ทั้ง 2 ตัวนี้ จึงแนะนำให้กำหนดเป็นชื่อตัวแปรไว้แบบนี้แทน

val appContext: Context = InstrumentationRegistry.getInstrumentation().targetContext
val testContext: Context = InstrumentationRegistry.getInstrumentation().context
และเนื้อหาต่อจากนี้เจ้าของบล็อกจะใช้คำว่า appContext และ testContext แทน

เมื่อ Context ต่างกัน Resource ที่มีให้เรียกใช้งานก็จะต่างกัน

โดย appContext จะเป็น Resource ใน Source Set ที่ชื่อว่า main

  • src/main/res
  • src/main/resources
  • src/main/assets

ส่วน testContext จะเป็น Resource ใน Source Set ที่ชื่อว่า androidTest

  • src/androidTest/res
  • src/androidTest/resources
  • src/androidTest/assets

ดังนั้นการเข้าถึงข้อมูลจำพวก Resource เหล่านี้นอกจากจะต้องกำหนด Resource ID / Path ให้ถูกต้องแล้ว ก็จะต้องเรียกใช้งาน Context ให้ถูกต้องด้วยเช่นกัน ไม่เช่นนั้นจะเกิดปัญหา NotFoundException ขึ้นได้

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