JUnit4를 이용한 Room DabaBase Test

2023. 4. 23. 02:47android

JUnit4를 이용한 Room DabaBase Test

지난번 Viewmodel, Repository의 unit test에 이어서 오늘은 room database의 instrumentation Test를 진행하는 방법을 작성해 보겠습니다. room database를 test 하기 위해 겪은 과정, 이슈들을 다뤄보겠습니다. 

 

Dependency는 아래 포스트의 Dependency를 복붙 하시면 됩니다!

2023.04.21 - [android] - JUnit4를 사용한 Viewmodel , usecase , repository 패턴 Unit Test

 

JUnit4를 사용한 Viewmodel , usecase , repository 패턴 Unit Test

안녕하세요 이번 편에서는 JUnit4를 베이스로 mockk와 함께 mvvm 패턴이 적용된 프로젝트를 unit test 하는 방법에 대해 알아보도록 하겠습니다. 부족한 점이 많아 이해가 안 되시거나 잘못된 점이 있

dev-musa.tistory.com


Room Database Instrumentation Test

room db의 테스트를 진행하기 위해 먼저 파일을 만들어줍니다.

위치는

다른 위치에 만들게 되면 오류가 발생하므로 꼭 androidTest 폴더에서 만들어야 합니다.

그리고 파일이름은 아무거나 상관없으니 만들어주시면 준비 끝!

 

그리고 필요한 게 2가지 있습니다. 

 

1. Database 

2. Dao


Database

먼저 room을 사용하기 때문에 당연히 db 연결을 위한 Database 클래스를 만들어줘야 합니다.

여기서 dao 도 지정해 줍니다.

@Database(
    entities = [Note::class],
    version = 1,
    exportSchema = false
)
abstract class NoteDataBase : RoomDatabase() {
    companion object{
        const val DB_NAME = "noteDB"
    }
    abstract fun dao() : Dao
}

Dao

Dao 인터페이스를 만들어줍니다.

@Dao
interface Dao {

    @Query("SELECT * FROM note")
    fun getNotes(): Flow<List<Note>>

    @Query("SELECT * FROM note WHERE id= :id")
    suspend fun getNote(id: Int) : Note

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insertNote(note: Note)

    @Delete
    suspend fun deleteNote(note: Note)

}

Test  클래스

테스트 함수를 보겠습니다. 

@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(AndroidJUnit4::class)
class NoteDatabaseDAOTest {

    private lateinit var noteDatabase: NoteDataBase
    private lateinit var noteDao: Dao

    @Before
    fun setUp(){
        val context = ApplicationProvider.getApplicationContext<Context>()
        noteDatabase = Room.inMemoryDatabaseBuilder(context, NoteDataBase::class.java).build()
        noteDao = noteDatabase.dao()
    }

    @After
    fun closeDB(){
        noteDatabase.close()
    }


    @Test
    fun insertTest() = runTest {
        //given
        val note = Note(0,"0","0",0L,0)
        
        //when
        noteDao.insertNote(note)
        
        //then
        val result = noteDao.getNotes().first()
        Assert.assertEquals(1, result.size)
    }

}

 

어노테이션은 코루틴 테스트를 위한 것이고 runner를 지정해 줍니다.

 

SetUp을 보시면 db를 만들 때 context가 필요합니다. Context 객체데이터베이스 생성 시 리소스에 대한 액세스를 제공하고, 데이터베이스를 생성하는 데 필요한 애플리케이션의 상태 정보를 가져오는 데 사용됩니다. 그런 이유로 context를 생성하고 noteDatabase를 inMemoryDatabaseBuilder를 통해 만들어줍니다. inMemoryDatabaseBuilder의 역할디스크에 저장하지 않고 메모리 상에서만 동작하는 DB를 만들 수 있게 합니다. 뒤에 .allowMainThreadQueries() 사용하기도 하는데 이 메서드의 역할은 메인스레드에서 DB에 접근할 수 있게 합니다. 왜 메인스레드에서 접근하게 하냐?  원래 메인스레드에서는 UI를 업데이트합니다. UI 업데이트 말고 데이터베이스나 네트워크 같은 무거운 작업을 메인스레드에서 처리하게 되면 스레드가 무거워져서 작동성이 떨어지거나 에러가 발생할 수 있습니다. 그래서 별도의 스레드에서 무거운 작업들인 DB나 네트워크 같은 작업을 처리합니다. 하지만 테스트를 위해 간단한 동작을 그냥 메인스레드를 통해 처리한다고 허용하여 작업을 간편하게 하기 위해 사용합니다. 

 

@Before
fun setUp(){
    val context = ApplicationProvider.getApplicationContext<Context>()
    noteDatabase = Room.inMemoryDatabaseBuilder(context, NoteDataBase::class.java).build()
    noteDao = noteDatabase.dao()
}

insert 테스트

note 객체를 하나 생성해서 insert함수를 실행합니다. 그리고 결과를 확인합니다. 간단하게 결과를 확인하려고 size만 체크하였는데 결과를 다른 방식으로 체크하시길 추천드립니다. 

@Test
fun insertTest() = runTest {

    //given
    val note = Note(0,"0","0",0L,0)
    
    //when
    noteDao.insertNote(note)
    
    //then
    val result = noteDao.getNotes().first()
    Assert.assertEquals(1, result.size)
    
}

 

구현하는 과정에 어려운 부분은 크게 있지 않습니다. 발생할 수 있는 에러라면 빌드 이슈이거나 오타 정도가 있을 것 같습니다. 사실 저는 unit test 폴더에 넣어두고 왜 계속 실행이 안되지 하면서 머리를 싸맸는데 이런 실수 안 하시길 바랍니다...

 

이상으로 room database 하는 방법을 알아보았습니다. 읽어주셔서 감사합니다!