본문 바로가기

Android

Android Test Case 개발 방법

안녕하세요.


Android Test Case 를 작성하는 방법에 대해서 가볍게 알아보도록 하겠습니다.


주의하세요! 자세한 코드의 설명은 누락되어 있습니다.


전체 소스는 아래 링크를 통해서 확인하실 수 있습니다.

Sources


이번에 다뤄볼 예제는 총 4가지입니다. 


1. UnitTest

2. Uiautomator2

3. Espresso

4. Robolectric


설명하기 전 어플리케이션의 동작부터 간단히 확인해 보도록 하겠습니다. 

- 타이틀이 보여지는 List 가 있으며 3개의 아이템이 존재합니다. 
- "aaa" 를 클릭하면 다음화면으로 Activity 가 실행되고 클릭한 문자열를 화면에 보여줍니다. 

 


1. UnitTest

[예제]


UnitTest를 사용하는 이유는 Android Framework를 의존하지 않고  작은 단위로 검증하기 위해서 입니다. 


그러면 예제를 확인해 보도록 하겠습니다.


SampleRepository.search("a") 함수를 호출하면 startWith("a") 의 값을 반환하도록 하는 예제입니다.


원본 데이터 값은 아래와 같은 List로 구성되어 있습니다.

listOf(
        Sample("aaa"),
        Sample("abb"),
        Sample("acc"),
        Sample("bcc")
    ) 


search("a") // a 로 시작하는 title을 검색하였을 경우 expect 한 값과 맞는지 검증하는 Case를 설정하였습니다.

TestCase

1. "a"로 시작하는 title 을 검증합니다. 


   fun `Repository 비지니스 로직 검증`() {
        val expect = listOf(
            Sample("aaa"),
            Sample("abb"),
            Sample("acc")
        )
        Assert.assertEquals(expect, repository.search("a"))
    } 

지금의 예제는 간단하기 때문에 별도의 testing framework를 사용하지 않았습니다.

만약 서로 연관된 복잡한 의존성을 가진다면 Mockito 를 활용해 볼 수도 있습니다.


Mockito는 Espresso에서 다뤄보도록 하겠습니다. 


2. UIAutomator

[예제]


UIAutomator는 시스템 환경이나 여러 Activity 를 생성하며 Test 할 수 있는 뛰어난 Framework 입니다. 


uiautomatorviewer 를 통해 현재 View의 구성요소들을 확인할 수 있습니다.



uiautomatorview 의 사용법은 여기 에 통해 확인하실 수 있습니다. 


UiSelector 는 다양한 조건들을 통해서 View를 접근할 수 있도록 지원하고 있습니다. 



자! 예제를 한번 실행해 볼까요?

TestCase

1. "aaa" text 를 찾습니다. (timeout 3초)

2. 찾았다면 Click 합니다.

3. 다음 Activity에서 detailTitle 의 View id의 text 가 "aaa" 인지 검증합니다.




@Test
    fun testClickSampleItem() {
        uiDevice.wait(Until.findObject(By.text("aaa")), 3).click()
        val detailTitle = uiDevice.wait(Until.findObject(By.res(PACKAGE_NAME, "detailTitle")), 3).text
        Assert.assertEquals("aaa", detailTitle)
    }

더 자세한 학습을 원하시면 BasicSample 를 통해 예제를 확인하실 수 있습니다. 


3. Espresso

Espresso 는 Single Acitivity 에 대해서 UI Test 할 수 있는 Framework 입니다.

UIAutomator는 View의 특성을 접근할 수 없지만 Espresso 는 View의 특성을 자세하게 테스트 할 수 있습니다. 

예를 들면 BackgroundColor 값이나 Visible 상태 등 여러가지를 말이죠.


여기서 좀 더 많은 설명이 필요하지만,, 피드백을 받아서 의견을 드리도록 하겠습니다.


val fixture = listOf(
        Sample("aaa"),
        Sample("abb"),
        Sample("acc"),
        Sample("bcc"))

@Before
fun setup() {
    results.postValue(fixture)
    viewModel = mock(SampleViewModel::class.java)
    `when`(viewModel.results).then { results }
    `when`(viewModel.search(Mockito.anyString())).then { fixture }
    fragment = SampleListFragment()
    fragment.viewModelFactory = ViewModelUtil.createFor(viewModel)
    activityRule.activity.setFragment(fragment)

Mockito 사용하고 있습니다.

`when`(viewModel.results).then { results }

Mockito 사용하는 이유는 실제 네트워크 환경에서 서버로 부터 데이터를 가져오는 것은 TestCase를 작성하는데 있어서 적절하지 않습니다.

적절하지 않은 이유:
 - 테스트하는 서버에 부하를 줄 수 있습니다.
 - 설정하지 않은 환경 (네트워크) 에서 테스트하고자 하는 Production Code 범위를 벗어납니다. 

TestCase

1. RecyclerView에 "aaa" 의 Text 가 존재하는지 검증합니다. 


    
  @Test
    fun testPresentItems() {
        onView(listMatcher().atPosition(0)).check(matches(hasDescendant(withText("aaa"))))
    }

더 자세한 학습을 원하시면 GithubBrowser 를 통해 예제를 확인하실 수 있습니다. 


4. Robolectric


위에 Espresso 예제를 실행하는데 어느정도에 시간이 걸렸나요? 

빌드 -> 설치 -> 테스트 순서에 따라 많은 시간이 소요됩니다.

만약 TDD로 UI Test를 포함하여 개발한다면 하루종일 얼마나 많은 코드를 작성할 수 있을까요? 

Robolectric은 실제 에뮬레이터가 아니라 JVM 상에 에뮬레이터를 실행하여 좀 더 빠르게 테스트를 진행할 수 있도록 도와줍니다. 

TestCase

1. 첫번째 Item을 클릭했을 때 다음으로 실행될 Activity의 Intent를 검증합니다. 


    @Test
    fun testDetailActivityIntent() {
        val testSample = Sample("aaa")

        activity.sampleList.findViewHolderForAdapterPosition(0)?.itemView?.performClick()

        val nextIntent = shadowActivity.peekNextStartedActivityForResult().intent
        nextIntent.component shouldBe ComponentName(activity, SampleDetailActivity::class.java)
        nextIntent.getParcelableExtra

(SampleDetailActivity.KEY_SAMPLE) shouldBe testSample }


결론

상황에 맞도록 테스트 범위를 정하는 것이 중요하다.


iOS Test Code 는 아래 글에서 확인하실 수 있습니다.


소중한 시간내어 주셔서 감사합니다.




'Android' 카테고리의 다른 글

Android VS iOS 메모리 관리 방법  (0) 2019.03.19