Robolectric 은 4.0 이후 버전부터는 안드로이드 공식 테스트 라이브러리와 완벽하게 호환되도록 설계되었습니다.
AndroidX Test API 를 사용하면 Robolectric test 과 Instrumentation test 모두 공통으로 적용 가능하기 때문에 개발자에게 학습 부하를 줄여 줍니다.
TestRunner
TestRunner
는 테스트 패키지를 기기(또는 JVM)에 로드하고, 테스트를 실행하고, 테스트 결과를 리포팅하는 역할을 합니다.
Robolectric Test 에서는 자체적으로 RobolectricTestRunner
를 제공하고 있지만, AndroidX Test API 에 포함되어 있는 AndroidJUnit4
클래스와도 호환됩니다.
Robolectric
@RunWith(RobolectricTestRunner.class)
public class SandwichTest {
}
AndroidX Test
import androidx.test.ext.junit.runners.AndroidJUnit4;
@RunWith(AndroidJUnit4.class)
public class SandwichTest {
}
참고로 AndroidJUnit4
클래스는 androidx.test.ext
패키지에 있기에 이 모듈을 build.gradle
에 추가 해줘야 합니다. (androidx.test.runner 패키지에 있는 AndroidJUnit4 클래스는 deprecated 되었습니다.)
Application
대부분의 안드로이드 코드는 Context
중심으로 구성되어 있기 때문에, 대다수의 테스트에서 Application context
를 들고 있는 경우가 많습니다.
AndroidX Test
import androidx.test.core.app.ApplicationProvider;
@Before
public void setUp() {
Context context = ApplicationProvider.getApplicationContext();
}
Robolectric 문서에 나와있는 RuntimeEnvironment.application
은 deprecated 되었습니다.
☞ android api 29 version issue
안드로이드 API 29 버전에서 테스트 하기 위해서는 Java 9
버전 이상을 요구합니다. 그러나 Android Studio 3.5.3 버전까지만 해도 아직까지 JDK 9 을 지원하고 있지 않기 때문에 targetSdkVersion
이 29 인 경우,
- Edit Configuration 에서
Test run Configuration
만 JDK 9 로 바꿔주거나 @Config
어노테이션을 사용하여 테스트 환경을 API Version 29 미만으로 설정하는 방식으로 조치해야 합니다.
Activity
Robolectric 은 Robolectric.setupActivity()
를 제공합니다. 이는 사용자와 상호작용할 준비를 마친 Resumed
상태의 액티비티를 설정해줍니다.
또한 Robolectric.buildActivity()
도 있는데, 이는 ActivityController
를 리턴하여 개발자가 Activity Lifecycle 을 컨트롤 할 수 있게 해줍니다. 이를 잘 다루기 위해서는 Lifecycle 에 대한 정확한 이해가 뒷받침 되어야 하겠습니다. 잘못된 상태에서 Activity 를 사용하는 것은 의도치 않은 행동을 야기하거나 다른 테스트에 호환성 문제를 일으킬 수 있습니다.
AndroidX Test API 에서 제공하는 ActivityScenario
는 앞서 말한 두가지를 대체할 수 있습니다. ActivityScenario
는 Lifecycle 전환에 대해 보다 엄격한 기준을 두고 몇몇 상황에 대해 제한합니다. Instrumentation test 에서는 @Rule
어노테이션에 사용할 수 있는 ActivityScenarioRule
클래스도 있습니다.
Robolectric
@RunWith(AndroidJUnit4.class)
public class MainActivityTest {
@Test
public void testIntent() {
// given
Intent intent = new Intent();
ActivityController controller = Robolectric
.buildActivity(MainActivity.class, intent)
.setup();
// when
controller.pause().stop();
// then
assertEquals(controller.getIntent(), intent);
}
}
AndroidX Test
@RunWith(AndroidJUnit4.class)
public class ExampleUnitTest {
private Context context;
@Before
public void setUp() {
context = ApplicationProvider.getApplicationContext();
}
@Test
public void testScenario() {
// given
Intent intent = new Intent(context, MainActivity.class);
ActivityScenario scenario = ActivityScenario.launch(intent);
// when
scenario.moveToState(Lifecycle.State.CREATED);
// then
scenario.onActivity( activity ->
assertEquals(activity.getIntent(), intent) );
}
}
기본적으로 Rebolectric 에서 하는 테스트는 UI Thread 이기 때문에 UI 처리를 위한 별도의 동기화를 하지 않아도 됩니다만, ActivityScenario.onActivity()
의 경우 보다 안전하게 액티비티에 접근할 수 있도록 해줍니다.
Views
Robolectric 은 View 와 상호작용 하는 데 매우 제한된 API 를 제공합니다.
대표적으로 Activity.findViewById() 같은 메소드 정도는 사용할 수 있습니다.
@RunWith(AndroidJUnit4.class)
public class MainActivityTest {
@Test
public void testFindViewById() {
// given
ActivityScenario scenario = ActivityScenario.launch(MainActivity.class);
String helloText = "Hello, Robolectric!";
// when
scenario.moveToState(Lifecycle.State.CREATED);
// then
scenario.onActivity(activity -> {
TextView textView = activity.findViewById(R.id.hello_text);
textView.setText(helloText);
assertEquals(textView.getText().toString(), helloText);
});
}
}
Robolectric 4.0 버전
부터는 instrumentation test 에서 View 를 탐색하고 상호작용 하는 API 를 제공해주는 Espresso
를 지원합니다.
@RunWith(AndroidJUnit4.class)
public class MainActivityTest {
@Test
public void testWithEspresso() {
// given
ActivityScenario scenario = ActivityScenario.launch(MainActivity.class);
// when
scenario.recreate();
// then
// espresso
onView(withId(R.id.hello_text)).check(matches(withText("Hello World!")));
}
}
Fragments
AndroidX Test API 에서는 FragmentScenario
를 통해 안전하게 테스트할 수 있도록 지원 해줍니다.
FragmentScenario
를 사용하기 위해서는 androdx.fragment:fragment-testing
모듈을 앱 수준의 build.gradle
에 의존성을 추가해줘야 합니다.
dependencies {
testImplementation 'androidx.fragment:fragment-testing:1.2.2'
}
Fragment 를 테스트 하는 것은 어떤 Fragment 냐에 따라 나뉩니다.
- UI가 포함된 Graphical Fragment
- 이 경우에는
launchFragmentInContainer()
메소드를 호출해야 합니다.
그래야 FragmentScenario 가 Activity 의 Root View 에 attach 합니다.
- 이 경우에는
- UI가 없는 Non-graphical fragment
- 이 경우에는
launchFragment()
메소드를 호출해야 합니다.
그러면 FragmentScenario 가 Root View 를 갖지 않은 Activity 에 attach 합니다.
- 이 경우에는
프래그먼트를 launching 하고 나면 FragmentScenario
는 RESUMED
상태를 갖습니다.
그리고 이 상태는 Graphical Fragment 의 경우 사용자와 상호작용 할 수 있는 UI 가 그려진 상태이기 때문에 Espresso UI Test
를 수행할 수도 있습니다.
다음은 launchFragmentInContainer()
를 사용한 테스트 예제입니다.
@RunWith(AndroidJUnit4::class)
public class GraphicalFragmentTest {
@Test
public void testEventFragment() {
Bundle fragmentArgs = new Bundle();
fragmentArgs.putInt("selectedListItem", 0);
FragmentFactory factory = new FragmentFactory();
FragmentScenario scenario = launchFragmentInContainer<MyFragment>(fragmentArgs, factory);
onView(withId(R.id.text)).check(matches(withText("Hello World!")));
}
}
Robolectrics 에서 제공하던 SupportFragmentUtil
과 SupportFragmentController
은 Android P 부터 deprecated 되었습니다.
따라서 프래그먼트에 대한 테스트는 androidx 패키지에서 제공하는 FragmentScenario 를 통해 진행하면 되겠습니다.
'Android > Test' 카테고리의 다른 글
[Android Test] 5) Robolectric Shadows 이해하기 (0) | 2020.03.15 |
---|---|
[Android Test] 4) Robolectric 테스트 환경 설정하기(Configuration) (0) | 2020.03.14 |
[Android Test] 2) Robolectric 설치 및 환경 설정 (0) | 2020.03.12 |
[Android Test] 1) Robolectric 이란? (0) | 2020.03.11 |
[Android UI Test] 6) Espresso 를 활용한 WebView Test (0) | 2020.03.10 |