[Android] 클린 아키텍처로 비지니스 코드 테스트 작성하기

sundays·2023년 4월 18일
0

cleanArchitecture

목록 보기
4/6

이번에는 클린 아키텍처를 활용하여 테스트 코드를 작성할 것입니다. 테스트 코드를 작성하게 될때 클린 아키텍처를 도입해 놓았던 것이 도움이 됩니다. 의존성 주입을 할때 테스트 코드에서 직접 ROOM에 접근 하는 대신 다른 곳에서 비지니스 로직을 테스트 할 수 있게 적용할 것이기 때문입니다. 모든 테스트는 실제로 운영 API를 조회하는 것이 아니라 mockk 데이터들을 가지고 비지니스 로직을 단위 테스트 할 것인데 이때 비지니스 로직은 use case에 있는 것이므로 실제로 단위 테스트 하는 클래스가 됩니다.

실제 USECASE

실제 사례의 테스트 코드를 분석해보겠습니다. 제가 가진 USECASE는 다음과 같습니다.

class GetNotes(private val repository: NoteRepository) {
    operator fun invoke(noteOrder: NoteOrder = NoteOrder.Date(OrderType.Descending)): Flow<List<Note>> {
        return repository.getNotes().map { notes ->
            when (noteOrder.orderType) {
                is OrderType.Ascending -> {
                    when (noteOrder) {
                        is NoteOrder.Title -> notes.sortedBy { it.title.lowercase() }
                        is NoteOrder.Date -> notes.sortedBy { it.timestamp }
                        is NoteOrder.Color -> notes.sortedBy { it.color }
                    }
                }
                is OrderType.Descending -> {
                    when (noteOrder) {
                        is NoteOrder.Title -> notes.sortedByDescending { it.title.lowercase() }
                        is NoteOrder.Date -> notes.sortedByDescending { it.timestamp }
                        is NoteOrder.Color -> notes.sortedByDescending { it.color }
                    }
                }
            }
        }
    }
}

해당 코드에서는 선택할 수 있는 정렬 구분은 제목, 날짜, 색상입니다. 해당 구분을 기준으로 오름 차순 내림차순으로 정리 됩니다. 그렇기 때문에 이 코드의 대한 리턴 될 경우의 수는 6가지가 되겠습니다. 제가 검증할 테스트 코드에서는 이 6가지의 테스트 케이스만을 검증하게 됩니다

위의 비지니스 코드를 검증하기 위해 테스트 코드에서 케이스가 받게 되는 데이터들을 저장할 수 있는 곳을 선언을 해야합니다. 그렇기 때문에 의존성 주입이 등장합니다 이것은 테스트 에서 사용할 테스트 데이터 레포지토리 라고 합니다.

테스트 데이터 레포지터리

테스트 데이터 레포지토리에는 실제로 데이터를 적용하게 될 DB와 같은 동작을 하게 할 클래스입니다.
해당 클래스에서는 NoteRepository Interface를 상속받아 데이터를 임시로 저장할 수 있는 리스트를 선언하였습니다.

FakeNoteRepository.kt

class FakeNoteRepository : NoteRepository {

    private val notes = mutableListOf<Note>();
    
    override fun getNotes(): Flow<List<Note>> {
        return flow { emit(notes) }
    }
    ...
}

해당 Repository에 실제로는 getNotes() 와 함께 다양한 메서드를 상속받아 오버라이딩 해주고 있습니다. 하지만 이번 유닛 테스트에서는 getNotes() 기능만 사용할 것이기 때문에 생략하겠습니다.

테스트 코드 작성

이번에는 작성한 FakeNoteRepository를 가지고 테스트 케이스를 작성하겠습니다. 해당 코드에서는 @Before 어노테이션을 사용하여 테스트 환경에 맞는 데이터를 먼저 입력해주겠습니다.

GetNotesTest

	private lateinit var getNotes: GetNotes
    private lateinit var fakeNoteRepository: FakeNoteRepository

    @Before
    fun setUp() {
    	// 1. fakeRepository를 설치합니다.
        fakeNoteRepository = FakeNoteRepository()
        getNotes = GetNotes(fakeNoteRepository)
		
        // 2. Fake 데이터들을 미리 생성해줍니다.
        val notesToInsert = mutableListOf<Note>()
        ('a'..'z').forEachIndexed { index, c ->
            notesToInsert.add(
                Note(
                    title = c.toString(),
                    color = index,
                    content = c.toString(),
                    timestamp = index.toLong()
                )
            )
        }
		
        notesToInsert.shuffle()
        // 3. fake데이터들을 입력해줍니다
        runBlocking {
            notesToInsert.forEach { fakeNoteRepository.insertNote(it) }
        }
    }

먼저 데이터들을 6개의 테스트 코드로 작성해주기전에 해당 데이터들을 조회할 수 있게 가짜 데이터들을 입력해주는 코드를 작성하였습니다. 해당 @Before가 끝나면 그 후에 @Test코드가 자동으로 테스트 될 것입니다.

	@Test
    fun `Order notes by title ascending, correct order`() = runBlocking {
    	// 데이터들을 제목을 기준으로 오름차순으로 가져옵니다. 
        val notes = getNotes(NoteOrder.Title(OrderType.Ascending)).first()
		
        // 제목을 기준으로 오름차순(lessThan) 으로 비교하여 확인
        for (i in 0..notes.size - 2) {
            assertThat(notes[i].title).isLessThan(notes[i + 1].title)
        }
    }

해당 코드에서 getNotes() 는 가장 상위에 제가 USECASE로 두었던 그 비지니스 로직을 가지고 리턴하게 되겠습니다. 그렇기 때문에 제가 테스트 하고자 한 비지니스 로직을 검증하는 유닛테스트가 된 것입니다.

그렇다면 제가 테스트 하고자 하는 것이 색상을 기준으로 내림차순이면 어떤 방식의 테스트 코드가 등장하게 될까요?

	@Test
    fun `Order notes by color descending, correct order`() = runBlocking {
    	// 데이터들을 색상을 기준으로 내림차순으로 가져옵니다
        val notes = getNotes(NoteOrder.Color(OrderType.Descending)).first()
		
        // 색상을 기준으로 내림차순(GreaterThan) 으로 비교 하여 확인한다
        for (i in 0..notes.size - 2) {
            assertThat(notes[i].color).isGreaterThan(notes[i + 1].color)
        }
    }

유사하지만 다른 assert를 사용하고 있기 때문에 사용법에 주의해야 할 것 같습니다. 이런 내용과 흡사한 내용을 4개 더 추가해서 usecase를 작성하면 되겠습니다.

결론

해당 내용은 비지니스의 로직을 검증하기 위한 로컬 단위 테스트 입니다. 화면에서도 확인할 수있는 다른 내용의 단위 테스트도 가능하기 때문에 이 부분은 다음에 포스팅에 작성됩니다! 해당 코드들을 참고하시기 편하게 하기 위해 github 에 모두 포스팅 되어있습니다.

profile
develop life

0개의 댓글