1. 시작하기 전에
이전 Codelab에서는 단위 테스트와 계측 테스트를 모두 작성하고 실행하는 방법을 알아봤습니다. 이 Codelab에서는 테스트를 작성할 때의 권장사항과 테스트 관련 특정 Gradle 종속 항목을 추가하는 방법을 소개합니다. 단위 테스트와 계측 테스트를 더 많이 작성하는 연습도 하게 됩니다.
기본 요건
- Android 스튜디오에서 기존 프로젝트를 열었습니다.
- Android 스튜디오에서 단위 테스트와 계측 테스트를 작성했습니다.
- Android 스튜디오에서 프로젝트를 탐색해 봤습니다.
- Android 스튜디오에서
build.gradle
파일을 사용해 봤습니다.
학습할 내용
- 테스트 작성 기본사항
- 테스트 관련 Gradle 종속 항목을 추가하는 방법
- 계측 테스트로 목록을 테스트하는 방법
필요한 항목
- Android 스튜디오가 설치된 컴퓨터
- Affirmations 앱의 솔루션 코드
이 Codelab의 시작 코드 다운로드
이 Codelab에서는 Affirmations 앱 솔루션 코드에 계측 테스트를 추가합니다.
- 프로젝트에 제공된 GitHub 저장소 페이지로 이동합니다.
- 브랜치 이름이 Codelab에 지정된 브랜치 이름과 일치하는지 확인합니다. 예를 들어 다음 스크린샷에서 브랜치 이름은 main입니다.
- 프로젝트의 GitHub 페이지에서 Code 버튼을 클릭하여 팝업을 엽니다.
- 팝업에서 Download ZIP 버튼을 클릭하여 컴퓨터에 프로젝트를 저장합니다. 다운로드가 완료될 때까지 기다립니다.
- 컴퓨터에서 파일을 찾습니다(예: Downloads 폴더).
- ZIP 파일을 더블클릭하여 압축을 해제합니다. 프로젝트 파일이 포함된 새 폴더가 만들어집니다.
Android 스튜디오에서 프로젝트 열기
- Android 스튜디오를 시작합니다.
- Welcome to Android Studio 창에서 Open을 클릭합니다.
참고: Android 스튜디오가 이미 열려 있는 경우 File > Open 메뉴 옵션을 대신 선택합니다.
- 파일 브라우저에서 압축 해제된 프로젝트 폴더가 있는 위치로 이동합니다(예: Downloads 폴더).
- 프로젝트 폴더를 더블클릭합니다.
- Android 스튜디오가 프로젝트를 열 때까지 기다립니다.
- Run 버튼 을 클릭하여 앱을 빌드하고 실행합니다. 예상대로 작동하는지 확인합니다.
2. 시작 앱 개요
Affirmations 앱은 긍정의 단어와 짝지어진 이미지 목록을 사용자에게 보여주는 화면 하나로 구성됩니다.
3. 권장사항
테스트 코드는 애플리케이션의 비즈니스 로직과 다르게 표시되도록 설계되었습니다. 테스트에는 로직이 포함되면 안 되기 때문입니다. 로직을 테스트하기만 해야 합니다. 따라서 테스트에는 if
나 when
과 같은 조건문이나 for
나 while
과 같은 제어 흐름 문이 없어야 합니다. 값을 조작하거나 실제 계산을 실행해도 안 됩니다.
때에 따라 테스트에 이러한 사항이 필요할 수 있지만 일반적으로는 피해야 합니다. 이는 앱에서 테스트하려는 로직 유형이므로 테스트에 이러한 종류의 코드가 있으면 앱 코드가 실패할 수 있는 것과 같은 방식으로 실패할 수 있습니다.
단위 테스트는 테스트에 필요한 앱의 코드만 호출하고 코드 호출의 결과로 생긴 코드의 값이나 상태를 테스트해야 합니다. UI 테스트는 사용자 인터페이스의 예상 상태만 테스트해야 합니다. 이 개념을 충분히 이해하는 데 시간이 걸릴 수 있지만 괜찮습니다. 이러한 개념을 설명하는 데 도움이 되는 주제를 이후 Codelab에서 다룰 예정입니다. 그동안은 테스트를 더 많이 작성하면서 테스트 작성에 사용하는 접근 방식에 주의를 기울여야 합니다.
4. 테스트 디렉터리 만들기
이전 Codelab에서는 계측 테스트를 위한 androidTest
디렉터리를 만드는 방법을 알아봤습니다. androidTest
디렉터리와 test
디렉터리 모두를 위해 이 프로젝트에도 이 프로세스를 반복합니다. 두 디렉터리의 프로세스는 같으며, 유일한 차이점은 test
디렉터리의 경우 New Directory 드롭다운에서 androidTest/java가 아닌 test/java를 선택해야 한다는 점입니다. 새 디렉터리마다 com.example.affirmations라는 새 패키지를 만듭니다.
5. 계측 테스트 클래스 만들기
androidTest -> com.example.affirmations에서 AffirmationsListTests.kt
라는 새 클래스를 만듭니다.
Dice Roller 앱과 마찬가지로 Affirmations에는 단일 활동만 있습니다. 활동의 UI를 테스트하려면 활동이 실행되도록 지정해야 합니다. 이렇게 하는 방법을 직접 생각해낼 수 있는지 확인해 보세요.
- 테스트 실행기를 새로 만든 클래스에 추가합니다.
@RunWith(AndroidJUnit4::class)
- 기본 활동의 활동 시나리오 규칙을 만듭니다.
@get:Rule
val activity = ActivityScenarioRule(MainActivity::class.java)
- Affirmations 앱에는 이미지 목록과 각각의 긍정 문구가 표시됩니다. UI는 항목과의 상호작용(예: 클릭 또는 스와이프)을 허용하지 않습니다. 따라서 이 앱의 경우 계측 테스트는 정적 데이터만 테스트합니다.
scroll_to_item()
이라는 테스트 메서드를 만듭니다.@Test
로 주석을 달아야 합니다.
이 테스트는 목록에 포함된 특정 항목으로 스크롤해야 합니다. 아직 이 접근 방식은 다루지 않았습니다. 프로젝트에서 아직 참조하지 않는 메서드가 필요하기 때문입니다. 테스트를 계속하기 전에 테스트 종속 항목을 추가해야 합니다.
6. 계측 테스트 종속 항목 추가
앱 코드에서 사용할 Gradle 종속 항목을 추가하는 것에는 이미 익숙하실 겁니다. Gradle을 사용하면 특별히 단위 테스트와 계측 테스트를 위한 종속 항목을 추가할 수도 있습니다. app -> build.gradle에 있는 앱 수준 build.gradle
파일을 엽니다. 종속 항목 섹션에는 다음과 같은 세 가지 종류의 종속 항목 구현이 있습니다. implementation
, testImplementation
, androidTestImplementation
implementation
은 애플리케이션 자체에서 사용되는 종속 항목에 사용하고 testImplementation
은 단위 테스트에서 사용되는 종속 항목, androidTestImplementation
은 계측 테스트에서 사용되는 종속 항목에 사용합니다.
- 계측 테스트에서
RecyclerView
와 상호작용할 수 있도록 종속 항목을 추가합니다. 다음 라이브러리를androidTestImplementation
으로 추가합니다.
androidx.test.espresso:espresso-contrib:3.4.0
종속 항목은 다음과 같습니다.
dependencies {
...
androidTestImplementation
'androidx.test.espresso:espresso-contrib:3.4.0'
}
- 이제 프로젝트를 동기화합니다.
7. RecyclerView 테스트
- 프로젝트가 동기화되면
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
)를 전달합니다.
- 이제 원하는
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. 로컬 테스트 종속 항목 추가
- 이 Codelab 앞부분에서는 세 가지 유형의 종속 항목 구현을 설명했으며 계측 테스트 종속 항목을 추가했습니다. 이제 로컬 테스트 종속 항목을 추가합니다. app -> build.gradle로 이동하여 다음을 단위 테스트 종속 항목으로 추가합니다.
org.mockito:mockito-core:3.12.4
종속 항목은 다음과 같이 표시됩니다.
dependencies {
...
testImplementation 'org.mockito:mockito-core:3.12.4'
}
- 이제 프로젝트를 동기화합니다.
10. 어댑터 테스트
이 앱은 단위 테스트에 적합하지 않습니다. 테스트할 로직이 많지 않기 때문입니다. 그러나 향후 테스트를 위한 준비로 다양한 구성요소를 더 많이 테스트해 볼 수 있습니다.
- 단위 테스트 클래스에 다음 줄을 배치합니다.
private val context = mock(Context::class.java)
mock()
메서드는 프로젝트에서 방금 구현한 라이브러리에서 가져옵니다. 모의 테스트는 단위 테스트의 필수 요소지만 이 Codelab에서는 다루지 않습니다. 모의 테스트는 별도의 Codelab에서 더 자세히 살펴봅니다. Android에서 Context
는 현재 앱 상태에 관한 컨텍스트이지만 단위 테스트는 실제 기기가 아닌 JVM에서 실행되므로 Context
가 없다는 점에 유의하세요. mock 메서드를 사용하면 Context
의 '모의' 인스턴스를 만들 수 있습니다. 실제 기능은 없지만 컨텍스트가 필요한 메서드를 테스트하는 데 사용할 수 있습니다.
adapter_size()
라는 함수를 만들고 테스트로 주석을 답니다. 이 테스트의 목표는 어댑터 크기가 어댑터에 전달된 목록 크기인지 확인하는 것입니다. 이렇게 하려면ItemAdapter
인스턴스를 만들고Datasource
클래스의loadAffirmations()
메서드에서 반환한 목록을 전달합니다. 또는 새 목록을 만들어 테스트합니다. 단위 테스트의 경우 테스트에 고유한 자체 데이터를 만드는 것이 가장 좋으므로 이 테스트의 맞춤 목록을 만듭니다.
val data = listOf(
Affirmation(R.string.affirmation1, R.drawable.image1),
Affirmation(R.string.affirmation2, R.drawable.image2)
)
- 이제
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
- 이 메서드는 테스트해야 하는 메서드입니다. 이 메서드에서 반환된 값이 2단계에서 만든 목록 크기와 일치해야 합니다.
assertEquals()
메서드를 사용하여 목록 크기 값과 어댑터 크기 값을 비교합니다.
assertEquals("ItemAdapter is not the correct size", data.size, adapter.itemCount)
assertEquals()
메서드를 이미 잘 알고 있더라도 철저하게 줄을 살펴보는 것이 좋습니다. 첫 번째 매개변수는 테스트가 실패하면 테스트 결과에 표시되는 문자열입니다. 두 번째 매개변수는 예상값입니다. 세 번째 매개변수는 실제값입니다. 테스트 클래스는 다음과 같이 표시됩니다.
- 이제 테스트를 실행합니다.
11. 솔루션 코드
12. 축하합니다
이 Codelab에서 알아본 내용은 다음과 같습니다.
- 테스트 관련 종속 항목을 추가하는 방법을 알아봤습니다.
- 계측 테스트로
RecyclerView
와 상호작용하는 방법을 알아봤습니다. - 기본적인 테스트 권장사항을 알아봤습니다.