Robolectric은 에뮬레이터나 실제 기기 없이 JVM에서 직접 테스트를 실행할 수 있는 Android 애플리케이션 단위 테스트 프레임워크입니다. Android SDK 호출을 가로채고 실제 컴포넌트의 동작을 에뮬레이트하는 섀도우 객체를 제공합니다. Robolectric Documentation, 2025에 따르면, 섀도우 객체는 약 15,000개의 Android API 클래스를 대체하여 플랫폼에서 테스트를 격리합니다.
핵심 사항
Robolectric은 2010년에 Android 애플리케이션 테스트를 가속화하기 위해 만들어진 오픈소스 프레임워크입니다. 에뮬레이터나 기기에서 실행하는 대신, Robolectric은 Android SDK 클래스의 섀도우 구현을 제공하여 JVM(Java Virtual Machine)에서 직접 실행합니다. 이를 통해 수천 개의 테스트를 초 단위로 실행할 수 있습니다.
전통적인 Android 테스트는 에뮬레이터에서 애플리케이션을 실행해야 하며, APK 빌드, 설치 및 실행에 3~10분이 소요됩니다. Robolectric은 이 단계를 제거합니다. 테스트는 일반 Java/Kotlin 테스트로 컴파일되어 JUnit을 통해 실행됩니다. 이를 통해 분 단위가 아닌 초 단위의 피드백 루프를 제공합니다.
Robolectric은 API 16(Android 4.1)부터 최신 안정 릴리스까지 모든 Android 버전을 지원합니다. 각 버전에 대해 애플리케이션이 테스트되는 해당 플랫폼 버전의 동작을 에뮬레이트하는 섀도우 객체 세트가 제공됩니다.
Robolectric의 아키텍처는 클래스로더 교체를 통한 호출 가로채기 메커니즘을 기반으로 합니다. 테스트가 Android SDK 메서드를 호출하면 Robolectric이 호출을 가로채서 섀도우 객체로 전달합니다. 섀도우 객체는 Android 컴포넌트의 실제 동작을 모방하지만 네이티브 플랫폼 없이 작동하는 Java 클래스입니다.
섀도우 객체는 원래 Android 클래스의 주요 메서드를 구현합니다. 예를 들어, ShadowTextView는 getText(), setText(), getCurrentTextColor() 등의 메서드를 모방합니다. textView.setText("안녕하세요")가 호출되면 섀도우가 문자열을 저장하고 getText()가 호출될 때 반환합니다. 이렇게 하면 테스트가 실제 렌더링 및 시스템 서비스로부터 격리됩니다.
Robolectric은 Android SDK의 주요 컴포넌트(Activity, Fragment, TextView, Button, RecyclerView, WebView, LocationManager, ConnectivityManager 등)를 포괄하는 200개 이상의 섀도우 클래스를 지원합니다. 섀도우 객체는 원래 Android 클래스와 동일한 상속 계층 구조를 따릅니다.
Gradle 프로젝트에 Robolectric을 설치하려면 testImplementation "org.robolectric:robolectric:4.x" 종속성을 추가해야 합니다. 또한 build.gradle 파일에 androidsdk 구성을 지정하고 android:testInstrumentationRunner를 추가해야 합니다. Robolectric을 사용하는 데 Android 테스트 플러그인이 필요하지 않습니다.
종속성을 추가한 후, 매니페스트, 리소스 및 에셋 디렉토리를 구성해야 합니다. Robolectric은 AndroidManifest.xml을 자동으로 찾지만, @Config 어노테이션이나 시스템 속성을 통해 경로를 수동으로 지정해야 할 수도 있습니다. 멀티 모듈 프로젝트의 경우 각 모듈마다 별도로 구성이 설정됩니다.
// build.gradle — Robolectric 구성
android {
testOptions {
unitTests.includeAndroidResources = true
unitTests.all {
systemProperty 'robolectric.dependency.dir',
project.rootDir.absolutePath + '/build/intermediates'
}
}
}
dependencies {
testImplementation 'org.robolectric:robolectric:4.13'
testImplementation 'org.junit.jupiter:junit-jupiter:5.10.0'
}
@Config 어노테이션은 테스트 매개변수(sdk: API 버전, manifest: 매니페스트 경로, qualifiers: 화면 구성, 언어)를 재정의할 수 있게 합니다. 이는 다양한 Android 버전, 화면 방향 및 로케일에서 애플리케이션 동작을 테스트하는 데 유용합니다.
Robolectric 테스트는 JUnit 4의 경우 @RunWith(RobolectricTestRunner.class), JUnit 5의 경우 RobolectricExtension을 사용하여 일반 단위 테스트처럼 작성됩니다. ActivityController는 Activity의 라이프사이클(create(), start(), resume(), pause(), stop(), destroy())을 관리합니다. 이를 통해 각 상태 전환을 세부적으로 테스트할 수 있습니다.
Activity를 테스트하려면 ActivityController를 사용하며, 라이프사이클 단계를 순차적으로 거칩니다. Activity에 대한 액세스는 controller.get()을 통해 이루어집니다. 모든 뷰 컴포넌트는 실제 애플리케이션에서와 같이 findViewById를 통해 액세스할 수 있습니다. 섀도우 객체를 사용하면 텍스트, 가시성, 색상 및 기타 뷰 속성을 확인할 수 있습니다.
@RunWith(RobolectricTestRunner.class)
@Config(sdk = BuildConfig.SDK_INT)
public class MainActivityTest {
@Test
public void testActivityDisplaysGreeting() {
ActivityController<MainActivity> controller =
Robolectric.buildActivity(MainActivity.class);
controller.create().start().resume();
MainActivity activity = controller.get();
TextView greeting = activity.findViewById(R.id.greeting);
ShadowTextView shadow = Shadows.shadowOf(greeting);
assertEquals("Welcome!", shadow.getText());
}
}
Robolectric은 startActivity() 호출을 가로채고 ShadowActivity를 통해 전송된 인텐트를 확인할 수 있습니다. 이를 통해 버튼 클릭 시 올바른 Action, Data 및 Extra 매개변수가 포함된 Intent가 전송되는지 확인하는 등 내비게이션 테스트가 가능합니다.
@Test
public void testNavigationToDetails() {
controller.create().start().resume();
MainActivity activity = controller.get();
activity.findViewById(R.id.detailsButton).performClick();
ShadowActivity shadowActivity = Shadows.shadowOf(activity);
Intent intent = shadowActivity.getNextStartedActivity();
assertEquals(
DetailsActivity.class.getName(),
intent.getComponent().getClassName()
);
}
Robolectric과 에뮬레이터는 다른 작업을 해결합니다. Robolectric은 빠른 단위 테스트, UI 로직, 비즈니스 컴포넌트 및 리포지토리 확인에 이상적입니다. 에뮬레이터는 계측 테스트, API와의 통합 확인, 카메라, 센서 및 성능 테스트에 필요합니다.
| 특성 | Robolectric | 에뮬레이터 |
|---|---|---|
| 속도 | 100개 테스트당 ~5초 | 100개 테스트당 ~5분 |
| 시작 | APK 빌드 불필요 | APK 빌드 필요 |
| 실제 API | 섀도우 에뮬레이션 | 네이티브 Android SDK |
| 기본 제공 | JUnit만 | 모든 프레임워크 |
Robolectric은 Android SDK를 사용하지만 실제 UI가 필요하지 않은 컴포넌트(ViewModel, Repository, UseCase) 테스트에 매우 적합합니다. ViewModel은 RobolectricExtension과 함께 일반 JUnit 테스트를 통해 테스트되며, 라이프사이클 컴포넌트(LiveData, StateFlow)는 목(mock) 없이 섀도우 환경에서 작동합니다.
Robolectric은 LiveData를 기본적으로 지원합니다. 테스트에서 LiveData를 구독하고, ViewModel을 통해 상태를 변경하고, 발행된 값을 확인할 수 있습니다. 비동기 작업에는 InstantTaskExecutorRule 또는 코루틴용 runBlocking이 사용됩니다. 이렇게 하면 아키텍처 컴포넌트를 목(mock)할 필요가 없습니다.
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [BuildConfig.SDK_INT])
class ProfileViewModelTest {
private val repository = FakeUserRepository()
private val viewModel = ProfileViewModel(repository)
@Test
fun `loading profile sets state to success`() = runBlocking {
viewModel.loadProfile("user123")
val state = viewModel.profileState.getOrAwaitValue()
Assertions.assertTrue(state is UiState.Success)
}
}
표준 Robolectric 섀도우 객체가 필요한 Android SDK 클래스를 포괄하지 않는 경우, 사용자 정의 섀도우를 만들 수 있습니다. 이렇게 하려면 @Implements(ClassName.class) 어노테이션이 있는 클래스를 만들고 @Implementation으로 필요한 메서드를 구현합니다. 사용자 정의 섀도우는 @Config(shadows = [CustomShadow::class])를 통해 등록됩니다.
Robolectric은 Robolectric.buildContentProvider()를 통한 ContentProvider 테스트를 지원합니다. CRUD 작업, URI 일치 쿼리 및 액세스 권한 처리를 확인할 수 있습니다. 리소스(문자열, 색상, 치수)는 RuntimeEnvironment.application.resources를 통해 액세스할 수 있어 리소스에 의존하는 코드를 테스트할 수 있습니다.
계측 테스트에서 Robolectric으로 전환하려면 접근 방식의 변경이 필요합니다. 계측 테스트(AndroidJUnit4)는 에뮬레이터에서 실제 동작을 확인하는 반면, Robolectric은 섀도우 환경에서 격리된 로직을 확인합니다. 비즈니스 로직은 Robolectric 테스트로 다루고 계측 테스트는 통합 시나리오용으로 남겨두는 것이 좋습니다.
마이그레이션 시 다음을 수행해야 합니다: AndroidJUnit4를 RobolectricTestRunner로 교체, 대상 SDK 버전으로 @Config 추가, ActivityTestRule을 ActivityController로 교체, 종속성을 androidTestImplementation에서 testImplementation으로 이동. 목(mock)(MockK, Mockito)은 변경 없이 Robolectric에서 작동합니다 — 플랫폼에 독립적입니다.
Robolectric은 다음을 지원하지 않습니다: 카메라(CameraX), NFC, Bluetooth, 생체 인식, 실제 파일 및 네이티브 라이브러리 작업. 이러한 시나리오에는 에뮬레이터에서의 계측 테스트가 필요합니다. Robolectric은 실제 렌더링도 재현하지 않습니다 — 레이아웃 테스트는 Compose Test Rule 또는 Espresso를 통해 수행하는 것이 좋습니다. 그러나 비즈니스 로직, 내비게이션 및 ViewModel 상태 확인의 경우 Robolectric은 10배 빠른 실행으로 에뮬레이터를 완전히 대체합니다.
Robolectric은 JDK 11 이상이 필요하며 AGP(Android Gradle Plugin) 7.0부터 최신 안정 버전까지 호환됩니다. Robolectric의 SDK 관리자는 첫 실행 시 필요한 Android API 버전을 자동으로 다운로드합니다(일회성 작업). CI 서버의 경우 첫 테스트 실행 지연을 피하기 위해 sdkmanager를 통해 SDK를 미리 로드하는 것이 좋습니다. Robolectric을 새 버전으로 업데이트해도 일반적으로 테스트 코드 변경이 필요하지 않으며, build.gradle의 종속성 번호만 업데이트하면 됩니다.
Robolectric은 인기 있는 모킹 프레임워크와 호환됩니다: Kotlin용 MockK 및 Java용 Mockito. 목(mock)은 리포지토리, API 클라이언트, SharedPreferences 등 종속성 격리에 사용됩니다. 주요 규칙은 Android SDK 클래스를 목(mock)하지 않는 것입니다 — 이를 위해 섀도우 객체가 있습니다. UseCase, Repository, DataSource 및 기타 비즈니스 로직 컴포넌트와 같은 애플리케이션 레이어만 목(mock)하세요. 섀도우 객체와 목(mock)의 이러한 조합은 최소한의 테스트 작성 노력으로 최대의 유연성을 제공합니다.
자주 묻는 질문
Espresso는 에뮬레이터나 기기에서 실행되는 계측 프레임워크입니다. Robolectric은 JVM에서 실행되는 단위 테스트 프레임워크입니다. Espresso는 실제 동작을 테스트하고, Robolectric은 섀도우 환경에서 격리된 로직을 테스트합니다. 두 프레임워크는 서로를 보완합니다.
네, Robolectric은 버전 4.8부터 Jetpack Compose를 지원합니다. Compose 컴포넌트 테스트는 에뮬레이터와 유사하게 ComposeTestRule을 사용하여 실행됩니다. 그러나 Robolectric의 Compose 테스트는 실제 렌더링을 확인하지 않고 컴포지션 로직만 확인합니다.
Robolectric은 멀티 모듈 프로젝트를 지원합니다. 각 모듈은 자체 매니페스트와 리소스로 별도 구성됩니다. UI 컴포넌트가 없는 모듈의 경우 Android 플러그인 없이 라이브러리 모듈로 충분합니다. 수십 개의 모듈이 있는 대규모 프로젝트에서는 각 모듈의 Robolectric 테스트가 병렬로 실행되어 에뮬레이터에서 순차 실행보다 추가적인 속도 이점을 제공합니다.
리소스 문제는 build.gradle에 unitTests.includeAndroidResources = true가 없거나 매니페스트의 올바른 경로가 있는 @Config 어노테이션이 없는 경우 발생합니다. Robolectric은 build/intermediates에서 컴파일된 리소스를 사용합니다.
Robolectric 테스트는 Android Studio에서 일반 Java/Kotlin 테스트처럼 디버그됩니다. 중단점, 단계별 디버깅, 섀도우 객체 및 해당 상태 검사가 가능합니다. 로깅은 VM 옵션에서 -Drobolectric.logging=debug를 통해 활성화됩니다.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.