Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/explore viewmodel 테스트 #34

Merged
merged 16 commits into from
Jan 11, 2025
Merged

Feature/explore viewmodel 테스트 #34

merged 16 commits into from
Jan 11, 2025

Conversation

Guri999
Copy link
Collaborator

@Guri999 Guri999 commented Jan 10, 2025

📝작업 내용

테스트 환경 세팅

mockk 추가

ExploreViewModel Test

3가지 Intent를 테스트 하는 코드 추가

@Guri999 Guri999 self-assigned this Jan 10, 2025
@Guri999 Guri999 requested a review from f-lab-dean January 10, 2025 15:04
@Guri999
Copy link
Collaborator Author

Guri999 commented Jan 10, 2025

테스트명을 한글로써서 sonarqube에 Issue로 되었는데
한글로 쓰는건 지양해야하나요??
한글로 써진 테스트 코드를 자주 봐가지구,

@f-lab-dean
Copy link

테스트명을 한글로써서 sonarqube에 Issue로 되었는데 한글로 쓰는건 지양해야하나요?? 한글로 써진 테스트 코드를 자주 봐가지구,

모든 코드는 기본적으로 영어입니다.
학습, 설명용 블로그글에 한글은 허용할 수 있는 범위지만, 실제 코드는 영어로 작성해주세요.

@@ -38,5 +38,27 @@ data class FileInfo(
createdAt = LocalDateTime.now(),
lastModified = LocalDateTime.now()
)

val PDF_DUMMY = FileInfo(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

테스트를 위한 코드는 테스트 패키지 또는 테스트 클래스 내에 작성해주세요.
실제 코드에 작성하면 불필요한 코드가 실제 바이너리에 포함되게 됩니다.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수정하였습니다

import java.time.LocalDateTime
import java.time.ZoneId

internal class FileManager: IFileManager {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

정답은 아닙니다만, 최근에 인터페이스에 I를 붙이지 않는 추세입니다.
아래와 같은 방식이 더 자주 보입니다.

interface FileManager 
class FileManagerImpl : FileManager

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수정하였습니다

import java.time.LocalDateTime
import java.time.ZoneId

internal class FileManager: IFileManager {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

사실 readPDFOrDirectory는 테스트가 불가능한 함수였습니다.
이처럼 테스트 가능한 설계에 대해 미리 고민하지 않으면 테스트 코드 작성시에 리팩토링을 해야하는 과정이 생깁니다. :)
이 경험을 통해 다음엔 더 효과적인 설계를 할 수 있도록 성장하기를 기대합니다.

import org.junit.Test
import kotlin.test.assertEquals

class ExploreViewModelTest {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

테스트 코드를 작성하다보면 코루틴 디스패처 정의나 Mockk 처리 등을 위한 코드가 중복이 됩니다.
그래서 이러한 것들을 Rule로 생성하고, 테스트 클래스를 위한 베이스를 만드는 것이 중복 제거와 생산성 향상에 좋습니다.
테스트코드 Rule에 대해서 학습해보세요.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Testing Module을 만들어 dispatcher에 관한 룰을 만들었습니다

@Before
fun setup() {
Dispatchers.setMain(testDispatcher)
recentRepository = mockk(relaxed = true)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mockk는 어노테이션을 제공합니다.
before 함수에 매번 mockk 코드를 작성하지 않고, 필요한 객체의 선언과 함께 모킹을 할 수 있습니다.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수정하였습니다

@Before
fun setup() {
Dispatchers.setMain(testDispatcher)
recentRepository = mockk(relaxed = true)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

relaxed가 무엇을 의미할까요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

반환값을 기본값으로 초기화 시키는 겁니다

}

@Test
fun `Init intent 경로를 통한 파일 초기화`() = testScope.runTest {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

테스트코드에서 코루틴을 사용할 때 StandardTestDispatcher, testScope가 필요한 이유가 무엇일까요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그냥 runTest 사용하면 될 것 같습니다


viewModel.handleIntent(ExploreUiIntent.Init(path))

advanceUntilIdle()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

advanceUntilIdle, runCurrent 등, 코루틴을 테스트할 때 사용되는 몇 가지들이 있습니다.
각각의 역할이 무엇인지 잘 이해하고 사용하시길 바랍니다.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

비동기 작업수행시 기다렸다 동작하는 것인데
delay가 아닌 모든 비동기 작업에서 수행해야 한다고 착각한것 같습니다


advanceUntilIdle()

viewModel.sideEffect.first().let {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

사소한 부분이지만, 어떤 객체의 생성과 함께 이어지는 검증을 해야할 때는 also가 용도에 좀 더 맞습니다만, 팀마다 견해가 다를 수 있습니다.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아 스코프 펑션을 너무 신경안쓰고 사용한것 같습니다

}

@Test
fun `Init intent 경로를 통한 파일 초기화`() = testScope.runTest {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

테스트코드의 명은 AAA 패턴 또는 Given-When-Then 을 따르면 이해하기 좋습니다.
특히, 나중에 kotest를 사용하시게 되면 더 강력하게 Given-When-Then을 따르게 될 수 있습니다.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수정하였습니다


internal class ExploreViewModel(
private val recentRepository: RecentRepository,
private val fileManager: FileManager,
private val fileManagerImpl: FileManagerImpl,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ViewModel의 의존성은 FileManager 인터페이스로 두고, 주입되는 클래스를 구체클래스인 FileManagerImpl로 하는 것이 의존성 주입 목적에 맞을 것 같습니다.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아 이름 바꾸면서 바꼇네요 수정하겠습니다

@@ -27,16 +25,16 @@ class ExploreViewModelTest {

private lateinit var viewModel: ExploreViewModel
private lateinit var recentRepository: RecentRepository
private lateinit var fileManager: FileManager
private lateinit var fileManagerImpl: FileManagerImpl

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ViewModel의 의존성을 FileManager 인터페이스로 변경하고 테스트 클래스도 동일하게 변경해주세요.

Copy link

@f-lab-dean f-lab-dean left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@Guri999 Guri999 merged commit 8b56145 into main Jan 11, 2025
2 checks passed
@Guri999 Guri999 deleted the feature/explore-test branch January 11, 2025 16:47
@Guri999 Guri999 mentioned this pull request Jan 13, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants