Android Instrumentation测试中出现多个DataStore异常



我有一个Android Instrumentation Test类,它应该测试我正在使用的DataStore。但是当我在类中添加多个测试时,我会得到以下错误:

java.lang.IllegalStateException: There are multiple DataStores active for the same file: /data/user/0/com.example.app/files/datastore/example_test.preferences_pb. You should either maintain your DataStore as a singleton or confirm that there is no two DataStore's active on the same file (by confirming that the scope is cancelled).
at androidx.datastore.core.SingleProcessDataStore$file$2.invoke(SingleProcessDataStore.kt:168)

这是测试类

private const val PREFERENCES = "example_test"
@RunWith(AndroidJUnit4::class)
class ExampleTest {
private val context = InstrumentationRegistry.getInstrumentation().targetContext
private val dataStore = PreferenceDataStoreFactory.create(
scope = CoroutineScope(Dispatchers.IO + SupervisorJob()),
produceFile = { context.preferencesDataStoreFile(PREFERENCES) }
)
@Test
fun testOne() {
runBlocking {
dataStore.edit {
it[stringPreferencesKey("bla")] = "bla"
}
}
}
@Test
fun testTwo() {
runBlocking {
dataStore.edit {
it[stringPreferencesKey("bla")] = "bla"
}
}
}
}

一旦我评论出testTwo(),它就会很好地工作。我不明白为什么要创建第二个DataStore。

JUnit将为每个测试创建一个测试类的新实例,从而创建上面的所有字段,以帮助确保它们之间没有可能导致不可靠、不一致测试的共享状态。

由于每次创建DataStore时都会创建它,但可能会被延迟清理(典型的JVM GC方法(,因此当它创建另一个DataStore时,过程中有多个活动的DataStore。这是不允许的,因为多个实例指向同一个文件可能会导致错误。

如果你上面的例子是从你的真实代码中精简出来的,并且在真实的事情中你正在测试你自己的代码,那么更有趣、更值得测试的事情是:包装数据存储,这样你就可以替换它。有了一个基本的首选项界面,你可以用一个简单的基于映射的实现来替换测试中的实现。然后,您还可以避免在测试中执行文件IO(通常是no,因为它很慢,并且在测试之间引入了共享状态(。

如果你真的只想测试实际的DataStore,那么答案就是不要写那个测试:不要测试库代码,只测试你自己的代码。

或者,你可能正在编写一个端到端/功能测试,并希望它尽可能接近于测试整个应用程序,而不需要大量的双重测试。在这种情况下,也许您真的想在测试中使用真实的数据存储。如果是这种情况,那么您可以使用文档倡导的属性delegate,因为它确保了一个单例,并确保它在测试类、全局范围之外(而不是在多个文件中(。那么它只会被调用一次。不过,在这种情况下,要警惕测试之间持续存在的状态;您可能希望在清除函数(@After(或测试规则中清除数据存储。

最新更新