반응형

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 인 경우,

  1. Edit Configuration 에서 Test run Configuration 만 JDK 9 로 바꿔주거나
  2. @Config 어노테이션을 사용하여 테스트 환경을 API Version 29 미만으로 설정하는 방식으로 조치해야 합니다.

 

Activity

RobolectricRobolectric.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 하고 나면 FragmentScenarioRESUMED 상태를 갖습니다.

그리고 이 상태는 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 에서 제공하던 SupportFragmentUtilSupportFragmentControllerAndroid P 부터 deprecated 되었습니다.


따라서 프래그먼트에 대한 테스트는 androidx 패키지에서 제공하는 FragmentScenario 를 통해 진행하면 되겠습니다.

 

 

반응형
반응형