JUnit을 사용한 단위 테스트
JUnit 4로 빠르고 안정적인 단위 테스트를 작성합니다. @Test, @Before, @After, 단언을 사용하고 InstantTaskExecutorRule로 LiveData를, runTest로 코루틴을 테스트합니다.
JUnit을 사용한 단위 테스트은(는) CoddyKit의 무료 Android Academy 강의입니다. 이것은 4개 중 1번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Android Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Android Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
왜 테스트를 작성해야 하나요?
테스트는 사치가 아니라 여러분의 안전망입니다:
- 사용자가 발견하기 전에 버그를 찾습니다
- 자신 있게 리팩터링할 수 있습니다(테스트가 통과하면 문제가 발생하지 않았다는 뜻입니다)
- 의도한 동작을 문서화합니다
- 기능을 추가할 때 기존 동작이 깨지는 것을 방지합니다
테스트가 잘된 코드는 설계도 더 나은 경우가 많습니다. 테스트하기 어렵다면 설계가 서로 지나치게 밀접하게 결합되었을 가능성이 큽니다.
Android 테스트 유형
두 가지 범주가 있습니다:
- 단위 테스트(
test/폴더) — JVM에서 실행되며 기기가 필요하지 않습니다. 빠르고, 일반적인 Kotlin/Java 로직을 테스트합니다. - 계측 테스트(
androidTest/폴더) — 기기나 에뮬레이터에서 실행됩니다. 더 느리며 Android 전용 코드(Room DB, UI)를 테스트합니다.
비즈니스 로직에는 단위 테스트를 우선 사용하세요. Android API와 상호작용해야 할 때만 계측 테스트를 사용하세요.
JUnit 4 설정
JUnit 4는 Android 프로젝트에 기본으로 포함되어 있습니다. app/build.gradle에 다음 항목을 추가합니다:
// app/build.gradle:
dependencies {
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3'
testImplementation 'androidx.arch.core:core-testing:2.2.0' // for LiveData
}첫 JUnit 테스트
src/test/java/에 클래스를 만들고 테스트 메서드에 @Test를 사용해 주석을 지정합니다:
import org.junit.Test
import org.junit.Assert.*
class CalculatorTest {
private val calc = Calculator()
@Test
fun `addition returns correct sum`() {
val result = calc.add(2, 3)
assertEquals(5, result)
}
@Test
fun `division by zero throws exception`() {
assertThrows(ArithmeticException::class.java) {
calc.divide(10, 0)
}
}
}@Before와 @After
각 테스트 전후에 상태를 설정하고 정리하려면 수명 주기 주석을 사용하세요:
class UserRepositoryTest {
private lateinit var repo: UserRepository
private lateinit var fakeDao: FakeUserDao
@Before
fun setUp() {
fakeDao = FakeUserDao() // fresh instance before each test
repo = UserRepository(fakeDao)
}
@After
fun tearDown() {
fakeDao.clear() // clean up after each test
}
@Test
fun `getUsers returns all users`() {
fakeDao.insert(User(1, "Alice"))
fakeDao.insert(User(2, "Bob"))
assertEquals(2, repo.getUsers().size)
}
}일반적인 검증
JUnit은 Assert.*에 검증 메서드를 제공합니다:
// Equality:
assertEquals(expected, actual)
assertNotEquals(unexpected, actual)
// Null:
assertNull(value)
assertNotNull(value)
// Boolean:
assertTrue(condition)
assertFalse(condition)
// Same object reference:
assertSame(expected, actual)
// Custom message on failure:
assertEquals("User ID should be 1", 1, user.id)
// Exception:
assertThrows(IllegalArgumentException::class.java) {
User(id = -1, name = "")
}ViewModel 테스트하기
LiveData를 테스트하려면 InstantTaskExecutorRule을 추가하여 LiveData가 동기적으로 게시되도록 합니다:
class UserViewModelTest {
@get:Rule
val instantTaskRule = InstantTaskExecutorRule()
private val fakeRepo = FakeUserRepository()
private lateinit var viewModel: UserViewModel
@Before
fun setUp() {
viewModel = UserViewModel(fakeRepo)
}
@Test
fun `users are loaded on init`() {
fakeRepo.usersToReturn = listOf(User(1, "Alice"), User(2, "Bob"))
viewModel.loadUsers()
val result = viewModel.users.value
assertNotNull(result)
assertEquals(2, result?.size)
}
}코루틴 테스트하기
테스트에서 코루틴 실행을 제어하려면 TestCoroutineDispatcher / StandardTestDispatcher를 사용하세요:
class SyncWorkerTest {
@get:Rule
val instantTaskRule = InstantTaskExecutorRule()
private val testDispatcher = StandardTestDispatcher()
@Before
fun setUp() {
Dispatchers.setMain(testDispatcher)
}
@After
fun tearDown() {
Dispatchers.resetMain()
}
@Test
fun `sync completes successfully`() = runTest {
val repo = FakeRepository()
val worker = SyncUseCase(repo, testDispatcher)
val result = worker.run()
assertTrue(result.isSuccess)
}
}가짜 객체와 모의 객체 비교
테스트에서 실제 의존성을 대체하는 두 가지 전략입니다:
- 가짜 객체 — 테스트를 위해 설계된 실제로 동작하는 구현입니다(예: 실제 데이터베이스 대신 메모리 내 목록 사용). 작성하기 쉽고 읽기 쉬우며 라이브러리가 필요하지 않습니다.
- 모의 객체 — 호출을 기록하고 상호작용을 검증할 수 있는 자동 생성 스텁입니다(Mockito, MockK). 더 강력하지만 지나치게 사용하면 변경에 취약해질 수 있습니다.
간단한 경우에는 가짜 객체를 우선 사용하고, 메서드 호출을 검증해야 할 때 모의 객체를 사용하세요.
가짜 객체 작성하기
가짜 객체는 실제 클래스와 동일한 인터페이스를 구현하지만 메모리 내 저장소를 사용합니다:
interface UserDao {
fun getAll(): List<User>
fun insert(user: User)
}
class FakeUserDao : UserDao {
private val storage = mutableListOf<User>()
override fun getAll(): List<User> = storage.toList()
override fun insert(user: User) { storage.add(user) }
fun clear() { storage.clear() }
}테스트 이름 규칙
좋은 테스트 이름은 무엇을 테스트하는지와 예상 결과를 설명합니다. 읽기 쉬운 설명을 위해 Kotlin에서는 백틱 함수 이름을 사용하세요:
`getUsers returns empty list when no users exist``login throws exception when password is blank``add returns correct sum for negative numbers`
패턴: `subject does X when Y`
빠른 확인
JUnit 테스트 클래스에서 @Before의 목적은 무엇인가요?
복습: JUnit 단위 테스트
신뢰할 수 있는 앱은 테스트된 앱입니다:
- 단위 테스트는
src/test/에 작성하고 JVM에서 실행합니다 — 빠릅니다 @Test를 사용해 주석을 지정하고assertEquals,assertTrue,assertThrows를 사용합니다- 설정과 정리에는
@Before/@After를 사용합니다 - LiveData를 동기적으로 테스트하려면
InstantTaskExecutorRule을 사용합니다 - 코루틴 테스트에는
runTest+StandardTestDispatcher를 사용합니다 - 간단한 대체 구현에는 모의 객체보다 가짜 객체를 우선 사용합니다
다음: 상호작용을 검증하기 위한 Mockito 모킹입니다.
자주 묻는 질문
“JUnit을 사용한 단위 테스트” 강의는 무료인가요?
네 — “JUnit을 사용한 단위 테스트” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Android Academy 강의 전체를 잠금 해제할 수 있습니다. Android Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
“JUnit을 사용한 단위 테스트”에서 뭘 배우나요?
JUnit 4로 빠르고 안정적인 단위 테스트를 작성합니다. @Test, @Before, @After, 단언을 사용하고 InstantTaskExecutorRule로 LiveData를, runTest로 코루틴을 테스트합니다. 브라우저에서 직접 실행하는 실습 코드로 Android Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
Android Academy을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 Android Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 1번째 강의입니다.
“JUnit을 사용한 단위 테스트” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 Android Academy 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 Android Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- JUnit을 사용한 단위 테스트
- Mockito를 사용한 모의 객체
- Espresso를 사용한 UI 테스트
- 디버깅과 프로파일링