Фон
Я запускаю Android инструментальные тесты для тестирования уровня базы данных моего приложения на основе Room. Слой базы данных включает Kotlin сопрограмм и LiveData. С помощью этого ответа StackOverflow я смог получить тесты, работающие с классами Dao, используя @Transaction
.
Исправление проблемы с транзакцией (проблема описана в ссылке выше). ) означало добавление этой строки в настройку базы данных в памяти:
db = Room
.inMemoryDatabaseBuilder(context, AppDatabase::class.java)
.setTransactionExecutor(Executors.newSingleThreadExecutor()) // <-- this
.build()
Проблема
С этим добавлением мои тестовые классы базы данных начали падать, когда они содержат несколько тестовых случаев, имеющих дело с с базой данных. Сообщение cra sh:
Test failed to run to completion. Reason: 'Instrumentation run failed due to 'Process crashed.''. Check device logcat for details
Test running failed: Instrumentation run failed due to 'Process crashed.'
Logcat немного более многословно (усечено):
2020-04-05 16:40:05.586 23377-23406/? E/AndroidRuntime: FATAL EXCEPTION: pool-2-thread-1
Process: org.example.pkg, PID: 23377
java.lang.RuntimeException: Exception while computing database live data.
at androidx.room.RoomTrackingLiveData$1.run(RoomTrackingLiveData.java:92)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:764)
Caused by: java.lang.IllegalStateException: Cannot perform this operation because the connection pool has been closed.
at android.database.sqlite.SQLiteConnectionPool.throwIfClosedLocked(SQLiteConnectionPool.java:1069)
at android.database.sqlite.SQLiteConnectionPool.waitForConnection(SQLiteConnectionPool.java:683)
Так что, похоже, Room пытается использовать соединение с базой данных, которое имеет был закрыт уже Я обнаружил, что если я перестану убирать за собой в методе @After
в тестовом классе, это будет исправлено:
@After
@Throws(IOException::class)
fun closeDb() {
// db.close() <-- with this disabled, no errors are thrown
}
Но это уродливое решение, потому что у меня будут утечки соединений с базой данных. База данных разрушена и установлена между каждым тестом. Я не понимаю, почему тест использует объект подключения другого теста.
Когда происходит этот cra sh, он разрушает всю структуру тестирования - больше не выполняется никаких методов тестирования.
Код
Код класса теста:
@RunWith(AndroidJUnit4::class)
class SomeRepositoryTest {
private lateinit var db: AppDatabase
@get:Rule
val instantTaskExecutorRule = InstantTaskExecutorRule()
@Before
fun createDb() {
val context = ApplicationProvider.getApplicationContext<Context>()
db = Room
.inMemoryDatabaseBuilder(context, AppDatabase::class.java)
.setTransactionExecutor(Executors.newSingleThreadExecutor())
.build()
}
@After
@Throws(IOException::class)
fun closeDb() {
db.close()
}
@Test
fun someTest() {
runBlocking {
topicRepository.insert(something)
}
val topicAsLiveData = topicRepository.getSomething()
}
}
Этот класс теста работает правильно, но если я несколько раз скопирую метод someTest()
, последующие запуски начнут давать сбой.
Что здесь происходит?