Computer >> 컴퓨터 >  >> 시스템 >> Android

Proto DataStore에 대한 마스터링 테스트:실용 가이드

Proto DataStore에 대한 마스터링 테스트:실용 가이드

이전 기사에서는 애플리케이션에서 Proto DataStore를 사용하는 방법을 설명했습니다. 나는 내 애플리케이션 중 하나에서 Proto DataStore를 사용한 경험의 일부로 이 기사를 썼습니다.

그 다음에는 제가 얻은 지식을 사용하여 해당 애플리케이션에서 Proto DataStore에 대한 테스트를 작성하는 것이 어떤 것인지 확인하고 싶었습니다.

온라인으로 지침을 검색해도 크게 안심할 수 없었기 때문에 지침을 찾고 있는 사람들을 위해 내 지식을 공유해야겠다고 생각했습니다. 최악의 경우 내 후손을 위한 일이 될 것입니다.

검색에서 이 기사를 찾았지만 주로 Proto DataStore가 아닌 Preferences 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"
}

$compose_version 프로젝트 수준 build.gradle 파일에 정의한 변수입니다.

그런 다음 androidTest로 이동하세요. 디렉터리를 만들고 새 파일을 만듭니다. 일반적으로 Proto DataStore와 상호작용하는 저장소 클래스가 있으므로 이 파일의 이름을 YourRepositoryClassNameTest로 지정할 수 있습니다. MyRepositoryTest이라는 이름을 사용하겠습니다. .

Proto DataStore 자체를 테스트하기 전에 이를 인스턴스화해야 합니다. 이에 대한 문서를 찾기 위해 온라인에 접속하면 드물다.

Proto 데이터 저장소 인스턴스화는 다음과 같이 전역 컨텍스트와 함께 사용됩니다(테스트 시나리오에서 사용되지 않는 경우):

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

테스트 클래스 내에서는 이 작업을 수행할 수 없습니다. 왜냐하면 위 코드를 복사하여 붙여넣을 수는 있지만 할 수는 없기 때문입니다. 액세스 데이터스토어 객체. 다음과 같이 애플리케이션 컨텍스트를 얻을 수 있습니다:

ApplicationProvider.getApplicationContext()

하지만 우리 myDataStore 이를 통해 개체를 사용할 수 없습니다.

그럼 우리는 무엇을 할 수 있나요?

위에 링크된 기사에는 PreferenceDataStoreFactory.create 메소드를 사용하여 Preference DataStore를 생성하는 방법에 대한 예가 있습니다.

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 
 )

우리는 testContext를 얻습니다 작성자:

private val testContext: Context = ApplicationProvider.getApplicationContext()

Proto DataStore를 성공적으로 생성한 후에는 이에 대한 몇 가지 테스트 작성으로 넘어갈 수 있습니다. Proto 데이터 저장소의 인스턴스를 종속성으로 받는 저장소 클래스가 있으므로 Proto 데이터 저장소를 생성한 후에는 저장소 클래스의 인스턴스를 생성해야 합니다.

 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) 
 }
 }
 }

☝️ 앞서 알아차렸을 수도 있지만 우리는 OptIn 주석을 사용하고 있습니다. 여기. 이는 (현재) 우리가 사용하고 있는 API가 실험적이므로 사용할 때 이를 표시해야 하기 때문입니다.

DataStore의 흐름에 액세스하고 있으므로 , testScope로 래핑해야 합니다. . TestScope 다음을 수행하여 생성됩니다:

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

이를 실행하고 첫 번째 Proto DataStore 테스트를 즐겨보세요.

약 2초간 즐거웠습니다.

좀 더 의미 있는 일을 해보자.

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. protobuff에서 노출된 API를 사용하여 테스트 항목을 생성합니다
  2. MyRepository 클래스에 노출된 메소드를 사용하여 Proto DataStore에 이 항목을 추가합니다.
  3. Proto DataStore가 노출하는 흐름에서 항목 목록을 가져옵니다
  4. Proto DataStore에서 찾은 항목이 이전에 생성한 항목과 일치하는지 확인합니다.

데이터 저장소에 누출이 있습니다

위의 테스트를 한 번에 실행하려고 하면 런타임 중에 곧 오류가 발생하게 됩니다.

동일한 파일에 대해 여러 개의 데이터 저장소가 활성화되어 있습니다:/data/user/0/com.example.app/files/datastore/dataStore_filename.pb. DataStore를 싱글톤으로 유지하거나 동일한 파일에 두 개의 DataStore가 활성화되어 있지 않은지 확인해야 합니다(범위가 취소되었는지 확인).

글쎄요, 그건 문제가 됩니다. 테스트 클래스에서는 DataStore 인스턴스를 하나만 생성했습니다.

여기서 무슨 일이 일어나고 있는 걸까요?

DataStore(Context.datastore를 의미)를 생성하기 위해 속성 위임을 사용하지 않기 때문에 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명 이상의 사람들이 개발자로 취업하는 데 도움을 주었습니다. 시작하세요