테스트를 위한 라이브러리 추가 및 확인하기
처음 안드로이드 프로젝트를 생성하고 gradle module을 열면 아래와 같이 테스트 관련 의존성 모듈을 확인할 수 있다.
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
- junit : 안드로이드, 자바, 코틀린을 위한 표준 테스팅 프레임워크
- ext.junit : 안드로이드 프레임워크에서 테스팅을 위한 확장 라이브러리
- espresso : 사용자 인터페이스(User Interface) 테스팅을 위한 라이브러리
여기에 한 가지 라이브러리를 더 추가한다.
testImplementation "com.google.truth:truth:1.0.1"
androidTestImplementation "com.google.truth:truth:1.0.1"
- truth : 테스트 단언문(Assertion)과 실패 메시지(Failure Message)의 가독성을 향상시켜 줄 수 있는 라이브러리
그런데, 동일한 라이브러리가 testImplementation와 androidTestImplementation 으로 각각 의존성을 추가하였다. 이게 어떤 의미인가? 두 개의 차이가 무엇인지 정리하고 계속 진행한다.
testImplementation와 androidTestImplementation의 차이?
안드로이드 프로젝트를 생성하게 되면 app > java 디렉토리 하위에 3개의 디렉토리가 표시되는 것을 알 수 있다. (아래 사진 참고)
가장 위에 표시된 디렉토리는 기본적으로 비지니스 로직 코드를 작성하는 디렉토리이다. 개발자가 안드로이드 어플리케이션 또는 라이브러리 등을 개발할 때 작성하는 코드는 해당 디렉터리에 표기된다. 다음 androidTest와 test 디렉토리로 구분할 수 있다.
androidTest 디렉토리는 안드로이드 프레임워크의 컴포넌트를 사용해서 테스트해야 하는 테스트 코드들이 작성된다. 즉 에뮬레이터 또는 타겟에서 동작시키는 테스트 코드로 앞선 글에서 설명한 Integrated Test 코드들이 여기 작성된다.
test 디렉토리는 안드로이드 프레임워크의 컴포넌트와 의존성이 없는 테스트 코드들을 작성하는 곳이다. 에뮬레이터가 아닌 JVM에서 테스트 할 수 있는 코드들이다.
위에서 androidTest와 test 디렉토리의 차이를 설명한 이유는 testImplementation와 androidTestImplementation 이 해당 디렉토리 들과 연관되어 있기 때문이다. testImpelention은 안드로이드 프레임워크와의 의존성이 없는 test 디렉토리에 적용되는 라이브러리들이다. androidTestImplentation은 androidTest와 관련된 라이브러리들이다. 그렇기에 사용자 인터페이스를 테스트하는 espresso 라이브러리는 androidTestImplementation을 이용하여 의존성을 추가한다.
간단한 테스트코드 작성하기
1. 함수 시그니처 생성하기.
테스트주도개발바(이하, TDD)하여 개발을 하기에, 먼저 함수 시그니처를 아래와 같이 생성한다. 아래 UserRegisterationUtil은 아디이와 비밀번호, 사용자 정보등을 입력받고 유효한지 검사한다.
object UserRegistrationUtil {
fun validateRegistrationInput(
id: String,
password: String,
confirmedPassword: String,
username: String,
age: Int
): Boolean {
return true
}
}
유효성은 아이디, 패스워드 등이 공백은 아닌지, 이미 존재하는 아이디 인지 등을 테스트한다.
2. 테스트케이스 작성하기
테스트케이스를 생성하는 가장 간단한 방법은 안드로이드 스튜디오에서 제공하는 기능을 사용하는 것이다. 테스트케이스를 생성하려는 클래스에서 마우스 오른버튼 > Gererate > Test... 를 클릭하면 아래와 같은 화면이 표시된다.
테스트는 androidTest가 아닌 test 디렉토리에 생성해야 합니다. 이유는 위에서 설명했듯이 해당 기능이 안드로이드 컴포넌트와 의존성이 없는, JVM에서 테스트할 수 있는 코드들이기 때문입니다. 생성한 코드는 아래와 같습니다.
class UserRegistrationUtilTest {
@Test
fun `Empty id returns false`() {
val result = UserRegistrationUtil.validateRegistrationInput(
"",
"test-password",
"test-password",
"James",
20
)
assertThat(result).isFalse()
}
@Test
fun `valid id and correctly repeated password returns true`() {
val result = UserRegistrationUtil.validateRegistrationInput(
"linuxias",
"test-password",
"test-password",
"James",
20
)
assertThat(result).isTrue()
}
}
테스트 함수의 이름은 `` 를 이용하여 생성이 가능하다. 실제 비지니스 로직에서는 사용이 불가능하지만 테스트 코드에서는 서술형 문장으로 함수명을 만들어서 더 직관적으로 사용할 수 있다. 코틀린 코딩 컨벤션에는 아래와 같이 설명이 되어있다.
Names for test methods
In tests (and only in tests), you can use method names with spaces enclosed in backticks.
모든 테스트를 작성하지는 않았지만, 위와 같이 2개 정도 정상적으로 로직이 동작하면 통과해야하는 테스트들을 작성하였다. 지금은 해당 함수의 로직이 구현되지 않았기에 테스트들을 실패할 것이다.
3. 함수 구현하기
package com.linuxias.unittesting
object UserRegistrationUtil {
private val existedID = listOf("exist-linuxias")
fun validateRegistrationInput(
id: String,
password: String,
confirmedPassword: String,
username: String,
age: Int
): Boolean {
if (id.isEmpty() || password.isEmpty()
|| confirmedPassword.isEmpty() || username.isEmpty())
return false
if (id in existedID)
return false
if (password != confirmedPassword)
return false
if (age <= 0)
return false
return true
}
}
함수 내부를 구현하고 테스트를 실행하면, 구현한 테스트케이스가 모두 통과됨을 알 수 있다.
결론
완벽한 예제는 아니지만, 간단한 함수와 해당 함수를 테스트하기 위한 몇 가지의 테스트케이스를 구현해보았다. 이걸 기반으로 안드로이드 애플리케이션 개발 시 다양한 테스트 상황에 대해 확장해 나가려한다. 기회가 된다면 직접 완벽하지 않은 예외처리와 테스트케이스를 추가해보는 걸 추천한다.
Reference
truth : https://github.com/google/truth
'Android > Testing' 카테고리의 다른 글
[Android/Testing] #6. 테스트 시작 전에 테스트 더블(Test Double) 이해하기 (0) | 2022.10.08 |
---|---|
[Android/Testing] #5. 테스트를 위한 기반 프로젝트 생성하기 (0) | 2022.10.02 |
[Android/Testing] #4. Room Database 테스트 (0) | 2022.09.18 |
[Android/Testing] #2. 좋은 테스트는 어떻게 작성해야 할까? (1) | 2022.09.12 |
[Android/Testing] #1. 왜 테스트를 해야 하는가? (0) | 2022.09.06 |