Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> ระบบ >> Android

การทดสอบความชำนาญสำหรับ Proto DataStore:คู่มือปฏิบัติ

การทดสอบความชำนาญสำหรับ Proto DataStore:คู่มือปฏิบัติ

ในบทความก่อนหน้านี้ ฉันได้อธิบายวิธีที่คุณสามารถใช้ Proto DataStore ในแอปพลิเคชันของคุณ ฉันเขียนบทความนั้นโดยเป็นส่วนหนึ่งของประสบการณ์ของฉันในการใช้ Proto DataStore ในแอปพลิเคชันของฉัน

หลังจากนั้น ฉันต้องการดูว่าการเขียนการทดสอบสำหรับ Proto DataStore ในแอปพลิเคชันนั้นเป็นอย่างไรโดยใช้ความรู้ที่ฉันได้รับ

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

ในการค้นหาของฉัน ฉันพบบทความนี้ แต่ส่วนใหญ่จะมุ่งเน้นไปที่การทดสอบ Preferences DataStore ไม่ใช่ Proto DataStore มันระบุว่า:

“อย่างไรก็ตาม โปรดทราบว่าคุณสามารถใช้เนื้อหานี้ในการตั้งค่า Proto DataStore การทดสอบ เนื่องจากจะคล้ายกับการตั้งค่าอย่างมาก”

แต่เมื่อปฏิบัติตามแล้ว ฉันพบว่านอกเหนือจากการขึ้นต่อกันแล้ว ที่นี่ไม่มีความคล้ายคลึงกันมากนัก และคุณจำเป็นต้องแนะนำตรรกะที่แยกจากกันเพื่อทดสอบ Proto DataStore ของคุณเอง

การตั้งค่าสิ่งต่างๆ

ในไฟล์ build.gradle ของแอปพลิเคชันของคุณ ให้เพิ่มการอ้างอิงต่อไปนี้:

dependencies {
 ///.....
 androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
 debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version"
}

04 คือตัวแปรที่คุณกำหนดไว้ในไฟล์ build.gradle ระดับโปรเจ็กต์ของคุณ

จากนั้นไปที่ 17 ของคุณ ไดเร็กทอรีและสร้างไฟล์ใหม่ โดยปกติ คุณจะมีคลาสพื้นที่เก็บข้อมูลที่โต้ตอบกับ Proto DataStore ของคุณ ดังนั้นคุณจึงสามารถตั้งชื่อไฟล์นี้เป็น YourRepositoryClassNameTest เราจะใช้ชื่อ 23 .

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

การสร้างอินสแตนซ์ Proto DataStore นั้นใช้กับบริบทส่วนกลางเช่นนั้น (เมื่อไม่ได้ใช้ในสถานการณ์การทดสอบ):

private val Context.myDataStore: DataStore<MyItem> by dataStore(
 fileName = DATA_STORE_FILE_NAME,
 serializer = MyItemSerializer
 )

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

ApplicationProvider.getApplicationContext()

แต่ 36 ของเรา วัตถุจะไม่สามารถใช้ได้ผ่านมัน

แล้วเราจะทำอย่างไร

ในบทความที่เชื่อมโยงด้านบน มีตัวอย่างวิธีที่เราสามารถสร้าง Preference DataStore โดยใช้เมธอด PreferenceDataStoreFactory.create

fun create( 
 corruptionHandler: ReplaceFileCorruptionHandler<Preferences>? = null, 
 migrations: List<DataMigration<Preferences>> = listOf(),
 scope: CoroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob()), 
 produceFile: () -> File): DataStore<Preferences>

แต่เนื่องจากเราไม่ได้ใช้ Preference DataStore นั่นจึงไม่เหมาะกับเรา สิ่งที่ได้ผลคือใช้เมธอด DataStoreFactory.create เช่นนี้:

fun <T : Any?> create( 
 serializer: Serializer<T>, 
 corruptionHandler: ReplaceFileCorruptionHandler<T>? = null, 
 migrations: List<DataMigration<T>> = listOf(), 
 scope: CoroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob()), produceFile: () -> File): DataStore<T>

มีข้อโต้แย้งหลายประการสำหรับวิธีนี้ (และบางข้อมีค่าเริ่มต้น) แต่เราไม่จำเป็นต้องส่งผ่านข้อโต้แย้งทั้งหมด เราจะส่งผ่าน:

  • คลาสซีเรียลไลเซอร์ของเรา
  • วิธีการแลมบ์ดาสำหรับการสร้างไฟล์สำหรับ Proto DataStore ของเรา
dataStore = DataStoreFactory.create( 
 produceFile = { 
 testContext.dataStoreFile(TEST_DATA_STORE_FILE_NAME) }, 
 serializer = MyItemSerializer 
 )

เราได้รับ 44 โดย:

private val testContext: Context = ApplicationProvider.getApplicationContext()

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

 private val repository = MyRepository(datastore)

ขั้นแรก เรามาสร้างการทดสอบเพื่อตรวจสอบสถานะ Proto DataStore เริ่มต้นของเรา Proto DataStore เองก็เปิดเผยโฟลว์ที่เราสามารถใช้ได้

@OptIn(ExperimentalCoroutinesApi::class)
 @Test
 fun repository_testFetchInitialState() {
 runTest {
 testScope.launch {
 val dataStoreObject = repository.myFlow.first()
 // Insert here whatever we want to assert from our
 // Proto DataStore. I.E. a flag whose initial value is false
 assert(dataStoreObject.myFlag == false) 
 }
 }
 }

JPOR คุณอาจสังเกตเห็นสิ่งนี้ก่อนหน้านี้ แต่เรากำลังใช้คำอธิบายประกอบ OptIn ที่นี่ เนื่องจาก (ปัจจุบัน) API ที่เราใช้อยู่ในขั้นทดลอง และต้องทำเครื่องหมายไว้เมื่อเราใช้งาน

เนื่องจากเรากำลังเข้าถึงโฟลว์ของ 55 ของเรา เราต้องล้อมมันไว้ใน 60 ของเรา . 77 ถูกสร้างขึ้นโดยการทำ:

@OptIn(ExperimentalCoroutinesApi::class)
private val dispatcher = TestCoroutineDispatcher()
@OptIn(ExperimentalCoroutinesApi::class)
private val testScope = TestCoroutineScope(dispatcher)

เรียกใช้และสนุกกับการทดสอบ Proto DataStore ครั้งแรกของคุณ

นั่นเป็นเรื่องสนุกประมาณสองวินาที

มาทำอะไรที่มีความหมายมากกว่านี้กันดีกว่า

ลองนึกภาพ Proto DataStore ของเรามีรายการอ็อบเจ็กต์อยู่ข้างใน และเราต้องการทดสอบสถานะของมันเมื่อเราเพิ่มรายการเข้าไป

@OptIn(ExperimentalCoroutinesApi::class)
 @Test
 fun repository_testAdditionOfItem() {
 runTest {
 testScope.launch {
 //1
 val item: MyItem = MyItem.newBuilder().setItemId(UUID.randomUUID().toString())
 .setItemDescription(TEST_ITEM_DESCRIPTION).build()
 //2
 repository.updateItem(item)
 //3
 val items = repository.myFlow.first().itemsList
 assert(items.size == 1)
 //4
 assert(items[0].itemDescription.equals(TEST_ITEM_DESCRIPTION))
 }
 }
 }
  1. เราสร้างรายการทดสอบโดยใช้ API ที่เปิดเผยจาก protobuff
  2. เราเพิ่มรายการนี้ไปยัง Proto DataStore โดยใช้วิธีที่เราเปิดเผยในคลาส MyRepository
  3. เรารวบรวมรายการจากโฟลว์ที่ Proto DataStore เปิดเผย
  4. เราตรวจสอบให้แน่ใจว่ารายการที่พบใน Proto DataStore ตรงกับรายการที่เราสร้างไว้ก่อนหน้านี้

DataStore ของคุณมีการรั่วไหล

หากคุณพยายามเรียกใช้การทดสอบข้างต้นในคราวเดียว ในไม่ช้า คุณจะได้รับข้อผิดพลาดระหว่างรันไทม์:

มี DataStore หลายตัวที่ใช้งานอยู่ในไฟล์เดียวกัน:/data/user/0/com.example.app/files/datastore/dataStore_filename.pb คุณควรรักษา DataStore ของคุณเป็นซิงเกิลตันหรือยืนยันว่าไม่มี DataStore สองอันที่ใช้งานอยู่ในไฟล์เดียวกัน (โดยยืนยันว่าขอบเขตถูกยกเลิก)

นั่นเป็นปัญหา เราสร้างอินสแตนซ์ DataStore เพียงอินสแตนซ์เดียวในคลาสทดสอบของเรา

เกิดอะไรขึ้นที่นี่?

เนื่องจากเราไม่ได้ใช้ตัวแทนคุณสมบัติเพื่อสร้าง DataStore ของเรา (หมายถึง Context.datastore) จึงไม่รับประกันว่าออบเจ็กต์ DataStore ของเราจะเป็นซิงเกิลตันทุกครั้งที่เราเข้าถึง

เพื่อหลีกเลี่ยงสถานการณ์นี้ ฉันพบว่าวิธีหนึ่งคือการลบและสร้าง DataStore ใหม่สำหรับแต่ละกรณีทดสอบ หากต้องการลบ DataStore เราสามารถทำได้ดังนี้:

@After
fun cleanup() {
 File(testContext.filesDir, "datastore").deleteRecursively()
}

และก่อนการทดสอบทุกครั้ง เราจะสร้างมันขึ้นมาใหม่:

@Before
 fun setup() {
 dataStore = DataStoreFactory.create(
 produceFile = {
 testContext.dataStoreFile(TEST_DATA_STORE_FILE_NAME)
 },
 serializer = MyItemSerializer
 )
 }

หากต้องการดูตัวอย่างเต็ม โปรดไปที่นี่

ในบทความนี้ ฉันต้องการแสดงโครงร่างของวิธีทดสอบ Proto DataStore

แม้ว่าฉันจะศึกษากรณีทดสอบสองกรณี ขึ้นอยู่กับ DataStore ของคุณและประเภทที่คุณกำหนดค่าไว้ที่นั่น อาจมีกรณีทดสอบและสถานการณ์จำลองเพิ่มเติมให้เขียน โครงสร้างมีอยู่ครบถ้วน คุณเพียงแค่ต้องปรับให้เข้ากับความต้องการของคุณ

เรียนรู้การเขียนโค้ดฟรี หลักสูตรโอเพ่นซอร์สของ freeCodeCamp ช่วยให้ผู้คนมากกว่า 40,000 คนได้งานในตำแหน่งนักพัฒนา เริ่มต้น