테스트 목록 및 어댑터

1. 시작하기 전에

이전 Codelab에서는 단위 테스트와 계측 테스트를 모두 작성하고 실행하는 방법을 알아봤습니다. 이 Codelab에서는 테스트를 작성할 때의 권장사항과 테스트 관련 특정 Gradle 종속 항목을 추가하는 방법을 소개합니다. 단위 테스트와 계측 테스트를 더 많이 작성하는 연습도 하게 됩니다.

기본 요건

  • Android 스튜디오에서 기존 프로젝트를 열었습니다.
  • Android 스튜디오에서 단위 테스트와 계측 테스트를 작성했습니다.
  • Android 스튜디오에서 프로젝트를 탐색해 봤습니다.
  • Android 스튜디오에서 build.gradle 파일을 사용해 봤습니다.

학습할 내용

  • 테스트 작성 기본사항
  • 테스트 관련 Gradle 종속 항목을 추가하는 방법
  • 계측 테스트로 목록을 테스트하는 방법

필요한 항목

  • Android 스튜디오가 설치된 컴퓨터
  • Affirmations 앱의 솔루션 코드

이 Codelab의 시작 코드 다운로드

이 Codelab에서는 Affirmations 앱 솔루션 코드에 계측 테스트를 추가합니다.

  1. 프로젝트에 제공된 GitHub 저장소 페이지로 이동합니다.
  2. 브랜치 이름이 Codelab에 지정된 브랜치 이름과 일치하는지 확인합니다. 예를 들어 다음 스크린샷에서 브랜치 이름은 main입니다.

1e4c0d2c081a8fd2.png

  1. 프로젝트의 GitHub 페이지에서 Code 버튼을 클릭하여 팝업을 엽니다.

1debcf330fd04c7b.png

  1. 팝업에서 Download ZIP 버튼을 클릭하여 컴퓨터에 프로젝트를 저장합니다. 다운로드가 완료될 때까지 기다립니다.
  2. 컴퓨터에서 파일을 찾습니다(예: Downloads 폴더).
  3. ZIP 파일을 더블클릭하여 압축을 해제합니다. 프로젝트 파일이 포함된 새 폴더가 만들어집니다.

Android 스튜디오에서 프로젝트 열기

  1. Android 스튜디오를 시작합니다.
  2. Welcome to Android Studio 창에서 Open을 클릭합니다.

d8e9dbdeafe9038a.png

참고: Android 스튜디오가 이미 열려 있는 경우 File > Open 메뉴 옵션을 대신 선택합니다.

8d1fda7396afe8e5.png

  1. 파일 브라우저에서 압축 해제된 프로젝트 폴더가 있는 위치로 이동합니다(예: Downloads 폴더).
  2. 프로젝트 폴더를 더블클릭합니다.
  3. Android 스튜디오가 프로젝트를 열 때까지 기다립니다.
  4. Run 버튼 8de56cba7583251f.png을 클릭하여 앱을 빌드하고 실행합니다. 예상대로 작동하는지 확인합니다.

2. 시작 앱 개요

Affirmations 앱은 긍정의 단어와 짝지어진 이미지 목록을 사용자에게 보여주는 화면 하나로 구성됩니다.

3. 권장사항

테스트 코드는 애플리케이션의 비즈니스 로직과 다르게 표시되도록 설계되었습니다. 테스트에는 로직이 포함되면 안 되기 때문입니다. 로직을 테스트하기만 해야 합니다. 따라서 테스트에는 ifwhen과 같은 조건문이나 forwhile과 같은 제어 흐름 문이 없어야 합니다. 값을 조작하거나 실제 계산을 실행해도 안 됩니다.

때에 따라 테스트에 이러한 사항이 필요할 수 있지만 일반적으로는 피해야 합니다. 이는 앱에서 테스트하려는 로직 유형이므로 테스트에 이러한 종류의 코드가 있으면 앱 코드가 실패할 수 있는 것과 같은 방식으로 실패할 수 있습니다.

단위 테스트는 테스트에 필요한 앱의 코드만 호출하고 코드 호출의 결과로 생긴 코드의 값이나 상태를 테스트해야 합니다. UI 테스트는 사용자 인터페이스의 예상 상태만 테스트해야 합니다. 이 개념을 충분히 이해하는 데 시간이 걸릴 수 있지만 괜찮습니다. 이러한 개념을 설명하는 데 도움이 되는 주제를 이후 Codelab에서 다룰 예정입니다. 그동안은 테스트를 더 많이 작성하면서 테스트 작성에 사용하는 접근 방식에 주의를 기울여야 합니다.

4. 테스트 디렉터리 만들기

이전 Codelab에서는 계측 테스트를 위한 androidTest 디렉터리를 만드는 방법을 알아봤습니다. androidTest 디렉터리와 test 디렉터리 모두를 위해 이 프로젝트에도 이 프로세스를 반복합니다. 두 디렉터리의 프로세스는 같으며, 유일한 차이점은 test 디렉터리의 경우 New Directory 드롭다운에서 androidTest/java가 아닌 test/java를 선택해야 한다는 점입니다. 새 디렉터리마다 com.example.affirmations라는 새 패키지를 만듭니다.

d762ecd8950e97b2.png

5. 계측 테스트 클래스 만들기

androidTest -> com.example.affirmations에서 AffirmationsListTests.kt라는 새 클래스를 만듭니다.

Dice Roller 앱과 마찬가지로 Affirmations에는 단일 활동만 있습니다. 활동의 UI를 테스트하려면 활동이 실행되도록 지정해야 합니다. 이렇게 하는 방법을 직접 생각해낼 수 있는지 확인해 보세요.

  1. 테스트 실행기를 새로 만든 클래스에 추가합니다.
@RunWith(AndroidJUnit4::class)
  1. 기본 활동의 활동 시나리오 규칙을 만듭니다.
@get:Rule
val activity = ActivityScenarioRule(MainActivity::class.java)
  1. Affirmations 앱에는 이미지 목록과 각각의 긍정 문구가 표시됩니다. UI는 항목과의 상호작용(예: 클릭 또는 스와이프)을 허용하지 않습니다. 따라서 이 앱의 경우 계측 테스트는 정적 데이터만 테스트합니다. scroll_to_item()이라는 테스트 메서드를 만듭니다. @Test로 주석을 달아야 합니다.

이 테스트는 목록에 포함된 특정 항목으로 스크롤해야 합니다. 아직 이 접근 방식은 다루지 않았습니다. 프로젝트에서 아직 참조하지 않는 메서드가 필요하기 때문입니다. 테스트를 계속하기 전에 테스트 종속 항목을 추가해야 합니다.

6. 계측 테스트 종속 항목 추가

앱 코드에서 사용할 Gradle 종속 항목을 추가하는 것에는 이미 익숙하실 겁니다. Gradle을 사용하면 특별히 단위 테스트와 계측 테스트를 위한 종속 항목을 추가할 수도 있습니다. app -> build.gradle에 있는 앱 수준 build.gradle 파일을 엽니다. 종속 항목 섹션에는 다음과 같은 세 가지 종류의 종속 항목 구현이 있습니다. implementation, testImplementation, androidTestImplementation

implementation은 애플리케이션 자체에서 사용되는 종속 항목에 사용하고 testImplementation은 단위 테스트에서 사용되는 종속 항목, androidTestImplementation은 계측 테스트에서 사용되는 종속 항목에 사용합니다.

  1. 계측 테스트에서 RecyclerView와 상호작용할 수 있도록 종속 항목을 추가합니다. 다음 라이브러리를 androidTestImplementation으로 추가합니다.
androidx.test.espresso:espresso-contrib:3.4.0

종속 항목은 다음과 같습니다.

dependencies {
    ...
    androidTestImplementation
'androidx.test.espresso:espresso-contrib:3.4.0'
}
  1. 이제 프로젝트를 동기화합니다.

7. RecyclerView 테스트

  1. 프로젝트가 동기화되면 AffirmationsListTests.kt 파일로 돌아갑니다. onView()로 작업을 실행할 ViewInteraction을 제공합니다. onView() 메서드를 사용하려면 ViewMatcher를 전달해야 합니다. withId()를 사용하여 긍정적 문구에 사용된 RecyclerView의 ID를 전달해야 합니다. 이제 ViewInteraction에서 perform()을 호출합니다. 여기에서 새로 추가된 종속 항목이 중요한 역할을 합니다. 이제 RecyclerViewActions.scrollToPosition<RecyclerView.Viewholder>(9) ViewAction을 전달할 수 있습니다.
onView(withId(R.id.recycler_view)).perform(
   RecyclerViewActions
       .scrollToPosition<RecyclerView.ViewHolder>(9)
)

이 줄의 문법을 파악하는 것은 중요하지 않지만 살펴보는 것이 좋습니다. 이름 RecyclerViewActions는 이름으로 의미를 정확히 알 수 있습니다. 즉, 테스트가 RecyclerView에서 작업을 실행하도록 하는 클래스입니다. scrollToPosition()은 지정된 위치로 스크롤할 RecyclerViewActions 클래스의 정적 메서드입니다. 이 메서드는 Generic이라는 것을 반환합니다. Generic은 이 Codelab에서 다루지 않지만 이 경우에는 RecyclerView의 항목이 무엇이든 반환하는 scrollToPosition() 메서드로 생각하면 됩니다.

앱에서 RecyclerView의 항목은 ViewHolder이므로 메서드 호출 뒤에 꺾쇠괄호 한 쌍을 배치하고 그 안에 RecyclerView.ViewHolder를 지정합니다. 마지막으로 목록의 최종 위치(9)를 전달합니다.

  1. 이제 원하는 RecyclerView 위치로 스크롤할 수 있으므로 UI에 예상 정보가 표시되는지 확인하는 어설션을 만듭니다. 마지막 항목으로 스크롤한 후 최종 긍정적 문구와 연결된 텍스트가 표시되는지 확인합니다. ViewInteraction으로 시작하지만 이번에는 새 ViewMatcher(이 경우에는 withText())를 전달합니다. 이 메서드에 마지막 긍정적 문구 텍스트가 포함된 문자열 리소스를 전달합니다. withText() 메서드는 표시되는 텍스트에 기반하여 UI 구성요소를 식별합니다. 이 구성요소의 경우에는 원하는 텍스트가 표시되는지 확인하기만 하면 됩니다. ViewInteraction에서 check()를 호출하면 됩니다. check()에는 matches() 메서드를 사용할 수 있는 ViewAssertion이 필요합니다. 마지막으로 isDisplayed() 메서드를 전달하여 UI 구성요소가 표시된다는 어설션을 만듭니다.
onView(withText(R.string.affirmation10))
    .check(matches(isDisplayed()))

스크롤할 위치의 하드 코딩에 관한 참고사항으로 돌아가 보면, RecyclerViewActions를 사용하여 이를 해결할 수 있는 방법이 나와 있습니다. 목록의 길이를 잘 모르는 경우 scrollTo 작업을 사용할 수 있습니다. scrollTo 함수에서 특정 항목을 찾으려면 Matcher<View!>!가 필요합니다. 여러 가지가 있을 수 있지만, 이 테스트의 목적에 부합하려면 withText를 사용합니다. 이를 방금 작성한 테스트에 적용하면 코드는 다음과 같습니다.

onView(withId(R.id.recycler_view)).perform(
   RecyclerViewActions
       .scrollTo<RecyclerView.ViewHolder>(
           withText(R.string.affirmation10)
       )
)

onView(withText(R.string.affirmation10))
    .check(matches(isDisplayed())
)

이제 테스트를 실행할 준비를 모두 마쳤습니다. 기기나 에뮬레이터에서 목록 하단으로 스크롤한 후 테스트가 통과하는 것을 확인할 수 있습니다. 테스트 결과가 정확한지 확인하려면 문자열 ID를 R.string.affirmation1로 바꿉니다. 스크롤한 후에는 이 문자열 리소스가 표시되지 않으며 테스트에 실패합니다.

RecyclerViewActions 클래스에서 사용 가능한 여러 메서드가 있으므로 사용 가능한 메서드를 살펴보는 것이 좋습니다.

8. 로컬 테스트 클래스 만들기

test -> com.example.affirmations에서 AffirmationsAdapterTests.kt라는 새 클래스를 만듭니다.

9. 로컬 테스트 종속 항목 추가

  1. 이 Codelab 앞부분에서는 세 가지 유형의 종속 항목 구현을 설명했으며 계측 테스트 종속 항목을 추가했습니다. 이제 로컬 테스트 종속 항목을 추가합니다. app -> build.gradle로 이동하여 다음을 단위 테스트 종속 항목으로 추가합니다.
org.mockito:mockito-core:3.12.4

종속 항목은 다음과 같이 표시됩니다.

dependencies {
    ...
    testImplementation 'org.mockito:mockito-core:3.12.4'
}
  1. 이제 프로젝트를 동기화합니다.

10. 어댑터 테스트

이 앱은 단위 테스트에 적합하지 않습니다. 테스트할 로직이 많지 않기 때문입니다. 그러나 향후 테스트를 위한 준비로 다양한 구성요소를 더 많이 테스트해 볼 수 있습니다.

  1. 단위 테스트 클래스에 다음 줄을 배치합니다.
private val context = mock(Context::class.java)

mock() 메서드는 프로젝트에서 방금 구현한 라이브러리에서 가져옵니다. 모의 테스트는 단위 테스트의 필수 요소지만 이 Codelab에서는 다루지 않습니다. 모의 테스트는 별도의 Codelab에서 더 자세히 살펴봅니다. Android에서 Context는 현재 앱 상태에 관한 컨텍스트이지만 단위 테스트는 실제 기기가 아닌 JVM에서 실행되므로 Context가 없다는 점에 유의하세요. mock 메서드를 사용하면 Context의 '모의' 인스턴스를 만들 수 있습니다. 실제 기능은 없지만 컨텍스트가 필요한 메서드를 테스트하는 데 사용할 수 있습니다.

  1. adapter_size()라는 함수를 만들고 테스트로 주석을 답니다. 이 테스트의 목표는 어댑터 크기가 어댑터에 전달된 목록 크기인지 확인하는 것입니다. 이렇게 하려면 ItemAdapter 인스턴스를 만들고 Datasource 클래스의 loadAffirmations() 메서드에서 반환한 목록을 전달합니다. 또는 새 목록을 만들어 테스트합니다. 단위 테스트의 경우 테스트에 고유한 자체 데이터를 만드는 것이 가장 좋으므로 이 테스트의 맞춤 목록을 만듭니다.
val data = listOf(
   Affirmation(R.string.affirmation1, R.drawable.image1),
   Affirmation(R.string.affirmation2, R.drawable.image2)
)
  1. 이제 ItemAdapter 인스턴스를 만들어 이전 단계에서 만든 context 변수와 data 변수를 전달합니다.
val adapter = ItemAdapter(context, data)

Recycler 뷰 어댑터에는 getItemCount()라는 어댑터 크기를 반환하는 메서드가 있습니다. 이 앱의 경우 메서드는 다음과 같습니다.

/**
* Return the size of your dataset (invoked by the layout manager)
*/
override fun getItemCount() = dataset.size
  1. 이 메서드는 테스트해야 하는 메서드입니다. 이 메서드에서 반환된 값이 2단계에서 만든 목록 크기와 일치해야 합니다. assertEquals() 메서드를 사용하여 목록 크기 값과 어댑터 크기 값을 비교합니다.
assertEquals("ItemAdapter is not the correct size", data.size, adapter.itemCount)

assertEquals() 메서드를 이미 잘 알고 있더라도 철저하게 줄을 살펴보는 것이 좋습니다. 첫 번째 매개변수는 테스트가 실패하면 테스트 결과에 표시되는 문자열입니다. 두 번째 매개변수는 예상값입니다. 세 번째 매개변수는 실제값입니다. 테스트 클래스는 다음과 같이 표시됩니다.

f81a27f5c1cf055e.png

  1. 이제 테스트를 실행합니다.

11. 솔루션 코드

12. 축하합니다

이 Codelab에서 알아본 내용은 다음과 같습니다.

  • 테스트 관련 종속 항목을 추가하는 방법을 알아봤습니다.
  • 계측 테스트로 RecyclerView와 상호작용하는 방법을 알아봤습니다.
  • 기본적인 테스트 권장사항을 알아봤습니다.