Room 라이브러리가 설정되어 있다고 가정하고 테스트 기록을 해보겠습니다.
Room 라이브러리에 대한 정보는 여기서 얻어갈 수 있습니다.
testImplementation "androidx.room:room-testing:2.4.1"
@Database(entities = [Statement::class], version = 1, exportSchema = false)
@TypeConverters(Converters::class)
internal abstract class CalculatorDatabase : RoomDatabase() {
abstract fun calculatorDao(): CalculatorDao
companion object {
@Volatile
private var INSTANCE: CalculatorDatabase? = null
fun getDatabase(context: Context): CalculatorDatabase {
return INSTANCE ?: synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
CalculatorDatabase::class.java,
"calculator_database"
).build()
INSTANCE = instance
instance
}
}
}
}
statement라는 엔티티를 저장하고 리스트를 가져오는 함수가 인터페이스에 설정되었습니다.
@Dao
internal interface CalculatorDao {
@Query("SELECT * FROM statement")
fun getAll(): Flow<List<Statement>>
@Insert
fun insertStatement(statement: Statement)
}
@Entity(tableName = "statement")
data class Statement(
@PrimaryKey val uuid: UUID,
val expression: String,
val calculateResult: String
)
@RunWith(AndroidJUnit4::class)
class CalculatorDaoTest {
private lateinit var appDatabase: CalculatorDatabase
Room DB 테스트는 DB객체를 생성할 때 context를 가져와야 하므로 Instrument 테스트가 진행되어야 합니다. 아래의 android 패키지 안에 설정해주세요.
@Before
fun setUp() {
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
appDatabase = Room.inMemoryDatabaseBuilder(
appContext,
CalculatorDatabase::class.java
).build()
}
@After
fun cleanup() {
appDatabase.close()
}
InstrumentationRegistry 객체는 androidTest 패키지에서 구현이 가능합니다. Room라이브러리는 inMemoryDatabaseBuilder라는 빌더 함수를 사용합니다. 이 함수는 프로세스가 실행될 때만 객체를 만들어주며 @After의 close()함수를 통해 DB 객체에 접근을 막을 수 있습니다.
@Test
fun insertStatement_checkSavedStatement() = runBlocking {
val statement = Statement(UUID.randomUUID(), "1 + 1", "2")
val job = launch(start = CoroutineStart.LAZY) {
appDatabase.calculatorDao().insertStatement(statement)
}
appDatabase.calculatorDao().getAll().take(1).test {
job.start()
Assert.assertEquals(statement, awaitItem().first())
}
}
Entity를 가져올 때 Flow객체를 이용해서 비동기 구현을 했습니다.
Flow 테스트를 위해 Turbine이라는 라이브러리를 이용하였습니다. runBlocking 코루틴 빌더를 만드면 그 안에 launch를 통해 Job객체를 만들 수 있습니다.
launch 람다 안에 statement를 저장합니다. 그리고 statement 리스트를 가져오는데 take를 통해 하나의 아이템만 가져오도록 설정합니다. 그러면 list중에 하나의 아이템만 반환이됩니다. take를 설정하지 않으면 아래와 같이 오류가 나올 수 있습니다.
app.cash.turbine.AssertionError: Unconsumed events found:
Assert 객체를 통해 검증해주면 테스트 코드가 완성이 됩니다!