단위 테스트 작성

1. 시작하기 전에

이전 Codelab에서는 Android 스튜디오로 프로젝트를 만들고 XML을 수정하여 앱에 맞춤설정된 UI를 만들며 비즈니스 로직을 수정하여 기능을 추가하는 방법을 알아봤습니다. 이 Codelab에서는 테스트가 중요한 이유에 중점을 두고 단위 테스트를 자세히 설명합니다. 단위 테스트의 모양과 작성하는 방법을 확인할 수 있습니다.

기본 요건

  • Android 스튜디오에서 프로젝트를 만들어 보았습니다.
  • Android 스튜디오에서 코드를 작성해 보았습니다.

학습할 내용

  • 테스트가 중요한 이유
  • 단위 테스트의 모양
  • 단위 테스트를 작성하고 실행하는 방법

필요한 항목

  • Android 스튜디오가 설치된 컴퓨터
  • 이 개발자 과정의 이전 Codelab에서 만든 프로젝트

2. 소개

Android 코드를 작성해 봤으므로 이제 테스트 코드를 살펴보겠습니다. 먼저 테스트에 관한 철학을 살펴본 후 Android 프로젝트에서 자동 생성된 테스트를 자세히 알아보고 마지막으로 Dice Roller 앱 테스트를 직접 작성합니다. 이 강의에서는 많은 내용을 다루지만 걱정하지 마세요. 천천히 내용을 익히면 됩니다. 테스트는 학습하는 데 많은 시간과 연습이 필요하기 때문입니다. 바로 이해되지 않아도 괜찮습니다.

테스트가 중요한 이유

처음에는 앱에 실제로 테스트가 필요하지 않은 것처럼 보일 수 있습니다. 앱이 작고 기능이 제한되어 있으면 수동으로 쉽게 테스트하여 모든 것이 올바르게 작동하는지 확인할 수 있습니다. 그러나 앱의 규모가 커지면서 수동 테스트는 자동 테스트를 작성하는 것보다 훨씬 많은 노력이 필요합니다. 또한 전문가 수준의 앱 작업을 시작하고 나면 테스트는 사용자층이 넓을 때 중요해집니다. 다양한 버전의 Android를 실행하는 여러 유형의 기기를 고려해야 합니다. 결국 자동 테스트가 수동 테스트보다 훨씬 빠르게 대다수 사용 시나리오를 고려할 수 있다는 결론에 도달하게 됩니다. 새 코드를 출시하기 전에 테스트를 실행하면, 예상치 못한 동작이 있는 상태로 앱이 출시되지 않도록 기존 코드를 변경할 수 있습니다. 자동 테스트는 소프트웨어를 통해 실행된 테스트입니다. 이와 달리 수동 테스트는 기기와 직접 상호작용하는 사람이 실행합니다. 자동 테스트와 수동 테스트는 제품 사용자에게 쾌적한 환경을 제공하는 데 중요한 역할을 합니다. 그러나 자동 테스트는 더 정확할 수 있고 팀의 생산성을 최적화합니다. 테스트를 실행하는 데 사람이 필요하지 않고 수동 테스트보다 훨씬 빠르게 실행할 수 있기 때문입니다.

단위 테스트 자세히 살펴보기

이 Codelab에서는 단위 테스트에 중점을 둡니다. 계측 테스트는 나중에 알아봅니다. 먼저 Android 스튜디오를 통해 Android 앱을 만들 때 생성되는 테스트를 살펴봅니다. 테스트를 실행하고 테스트 코드 작성에 익숙해지는 실습도 해 봅니다.

이전 개발자 과정에서는 테스트용 소스 파일을 찾을 수 있는 위치를 살펴봤습니다. 단위 테스트는 항상 test 디렉터리에 있습니다.

f02b380da4e8f661.png

  1. app/build.gradle 파일을 열고 종속 항목을 확인합니다. 일부 종속 항목이 testImplementationandroidTestImplementation으로 표시되어 있습니다. 이는 각각 단위 테스트와 계측 테스트에 해당합니다. 참고:

app/build.gradle

testImplementation 'junit:junit:4.12'

JUnit 라이브러리는 단위 테스트를 구동하고, 앱 코드를 테스트할 수 있는 방식으로 컴파일하고 실행할 수 있도록 코드를 테스트로 표시하도록 허용합니다.

  1. test 디렉터리에서 ExampleUnitTest.kt 파일을 엽니다.

샘플 단위 테스트가 다음과 같이 표시됩니다.

ExampleUnitTest.kt

class ExampleUnitTest {
   @Test
   fun addition_isCorrect() {
       assertEquals(4, 2 + 2)
   }
}

Dice Roller 앱에 코드를 추가했지만 테스트를 전혀 작성하지 않았을 수 있습니다. 따라서 Android 스튜디오에서 자동으로 생성되는 일반 코드만 몇 개 있습니다. 이는 개발자가 작성해야 하는 더 관련성 높은 테스트의 자리표시자 역할을 하는 임의 테스트입니다. 현재 이 코드 블록은 2 + 2 = 4만 테스트합니다. 물론 이는 항상 참입니다. 무슨 일이 발생하는지 자세히 살펴보세요.

  • 테스트 함수는 먼저 org.junit.test 라이브러리에서 가져온 @ Test로 주석을 달아야 합니다. 주석은 코드가 컴파일되는 방법을 변경할 수 있는 코드의 메타데이터 태그로 생각하면 됩니다. 이 경우 @Test 주석을 사용하면 컴파일러에서 다음 메서드가 테스트임을 알 수 있으므로 그와 같이 실행될 수 있습니다.

주석 뒤에는 함수 선언이 있습니다. 이 경우에는 addition_isCorrect() 함수입니다. 함수 내에서 assertEquals() 함수는 예상값이 비즈니스 로직을 통해 가져온 실제값과 같아야 한다고 어설션합니다. 어설션 메서드는 단위 테스트의 최종 목표입니다. 궁극적으로 코드에서 가져온 결과가 특정 상태에 있음을 어설션하는 것이 좋습니다. 결과 상태가 예상 상태와 일치하면 테스트가 통과됩니다. 결과 상태가 예상 상태와 일치하지 않으면 테스트가 실패합니다. 이 경우 코드는 두 값을 비교하므로 assertEquals() 메서드는 예상값과 실제값이라는 두 매개변수를 사용합니다. 이름대로 예상값은 특정 결과로 예상하는 값입니다. 이 경우에는 4입니다. 실제값은 실제 코드의 결과를 나타냅니다. 일반적으로 앱 자체에서 코드를 테스트합니다. 이 경우에는 임의의 코드(예: 2 + 2)일 뿐입니다. 이제 바로 이 테스트를 실행하여 결과를 확인하세요.

Android 스튜디오에서 테스트를 실행하는 방법에는 여러 가지가 있으며 나중에 자세히 알아봅니다. 지금은 간단하게 유지합니다.

  1. addition_isCorrect 메서드 선언 옆의 화살표를 클릭하고 Run ‘ExampleUnitTest.addition_isCorrect'를 선택합니다.

78c943e851a33644.png

이를 포지티브 테스트라고 합니다. 즉, 어설션이 긍정 상태입니다. 2 + 2는 4와 같습니다. 또는 어설션을 부정 상태로 만드는 네거티브 테스트를 작성할 수 있습니다. 예: 2 + 2는 5와 같지 않습니다.

Run 창이 다음 스크린샷과 같이 표시됩니다.

190df0c8ff787233.png

테스트에 성공했다는 다양한 표시가 있습니다. 즉, 녹색 체크표시와 통과한 테스트 수입니다.

aa7d361d8e4826ef.png

  1. 테스트를 수정하여 실패가 어떻게 표시되는지 확인합니다. 2 + 22 + 3으로 변경하고 테스트를 다시 실행합니다. 테스트 작동 방식을 알아보기 위해 생성된 코드로만 실험하고 있습니다. 이러한 변경사항은 Dice Roller 기능과 관련이 없습니다.

ExampleUnitTest.kt

class ExampleUnitTest {
   @Test
   fun addition_isCorrect() {
       assertEquals(4, 2 + 3)
   }
}

나머지를 실행하면 다음과 같은 스크린샷이 표시됩니다.

751ac8089cf4c47c.png

빨간색 텍스트는 테스트 실패를 나타냅니다. 테스트 결과 메뉴에서 항목을 클릭하면 테스트 실패 이유를 나타내는 오류 메시지가 제공됩니다.

163708373e651ecc.png

이 경우 메시지는 어설션이 4라는 결과를 예상했지만 실제값은 5였으므로 실패했다고 나타냅니다. 이는 실제값은 2 + 3으로 변경했지만 예상값은 4로 두었기 때문입니다. 테스트가 실패한 줄도 확인할 수 있습니다. 이 경우에는 ExampleUnitTest.kt:15로 표시된 15번째 줄입니다.

  1. 철저하게 하기 위해 예상값을 4에서 5로 변경하고 테스트를 다시 실행합니다. 이제 예상값이 문제가 되는 코드의 실제 결과와 일치하므로 테스트가 통과됩니다.

3. 첫 번째 단위 테스트 작성

단위 테스트를 알아봤으므로 이제 Dice Roller 앱과 더 관련성이 높은 단위 테스트를 직접 작성할 수 있습니다.

이미 살펴본 바와 같이 Dice Roller 앱의 기본 기능은 랜덤 숫자 생성기에 기반합니다. 안타깝게도 랜덤 숫자 생성기는 테스트하기가 매우 어렵습니다. 무작위로 생성된 숫자라는 결과를 확실히 알 수 없기 때문입니다. 이 테스트의 목표는 주사위를 굴리거나 dice 클래스에서 roll 메서드를 호출할 때 적절한 숫자가 반환되는지 확인하는 것입니다. 직접 작성하는 테스트는 랜덤 숫자 생성기의 출력이 생성기에 지정한 범위 내 숫자인지만 테스트합니다.

  1. ExampleUnitTest.kt 파일에서 생성된 테스트 메서드와 import 문을 삭제합니다. 이제 파일이 다음과 같이 표시됩니다.

c06e8b402f293b5e.png

  1. generates_number() 함수를 만듭니다.

ExampleUnitTest.kt

fun generates_number() {
}
  1. generates_number() 메서드에 @Test로 주석을 답니다. @Test를 호출하려고 하면 텍스트가 빨간색으로 표시됩니다. 이 주석 선언을 찾을 수 없기 때문이므로 주석을 가져와야 합니다. Control+Enter(Mac은 Options+Return)를 누르면 자동으로 실행됩니다.

코드 줄을 클릭하면 가져오기 메시지가 표시됩니다.

bbe5791b9565588c.png

또는 패키지 이름 뒤, 클래스 선언 앞에 import org.junit.Test 파일을 복사하여 붙여넣을 수도 있습니다. 이제 코드가 다음과 같이 표시됩니다.

9a94c2bdf84adb61.png

  1. Dice 객체의 인스턴스를 만듭니다.

ExampleUnitTest.kt

@Test
fun generates_number() {
   val dice = Dice(6)
}
  1. 다음으로 이 인스턴스에서 roll() 메서드를 호출하고 반환된 값을 저장합니다.

ExampleUnitTest.kt

@Test
fun generates_number() {
   val dice = Dice(6)
   val rollResult = dice.roll()
}
  1. 마지막으로 실제 어설션을 만듭니다. 즉, 전달한 면 수 내에 있는 값을 메서드가 반환했다고 어설션해야 합니다. 따라서 이 경우에는 값이 0보다 크고 7보다 작아야 합니다. 이렇게 하려면 assertTrue() 메서드를 사용합니다. assertTrue() 메서드를 호출하려고 하면 처음에는 텍스트가 빨간색이 됩니다. 이 메서드 선언을 찾을 수 없기 때문이므로 주석 작업에서와 마찬가지로 메서드를 가져와야 합니다.

10eea07fc21bf998.png

이전에 설명한 대로 자동으로 가져올 수 있습니다. 그러나 이번에는 선택할 수 있는 옵션이 여러 개 있습니다. 이 경우에는 org.junit.Assert 패키지의 옵션이어야 합니다.

5dbfba2ba0e37ac9.png

또는 테스트 주석의 import 문 뒤에 이 코드를 붙여넣을 수 있습니다.

ExampleUnitTest.kt

import org.junit.Assert.assertTrue

이제 코드가 다음과 같이 표시됩니다.

347f792f455ae6b5.png

괄호 사이에 커서를 놓고 Control+P(Mac은 Command+P)를 누르면 메서드에서 사용하는 매개변수를 보여주는 도움말이 표시됩니다.

865cf0ac47738e08.png

assertTrue() 메서드는 두 매개변수, StringBoolean을 사용합니다. 어설션에 실패하면 문자열이 콘솔에 표시되는 메시지입니다. 부울은 조건문입니다. 다음과 같이 메시지를 설정합니다.

ExampleUnitTest.kt

"The value of rollResult was not between 1 and 6"

앞서 언급했듯이 랜덤 숫자 테스트는 어렵습니다. 랜덤 숫자의 무작위 특성으로 인해 숫자의 값을 예측할 수 없기 때문입니다. 할 수 있는 일은 특정 범위 내에 값이 있는지 확인하는 것뿐입니다. 조건 매개변수를 다음과 같이 설정합니다.

ExampleUnitTest.kt

rollResult in 1..6

코드는 다음과 같습니다.

ExampleUnitTest.kt

@Test
fun generates_number() {
   val dice = Dice(6)
   val rollResult = dice.roll()
   assertTrue("The value of rollResult was not between 1 and 6", rollResult in 1..6)
}
  1. 함수 옆의 화살표를 클릭하고 Run ‘ExampleUnitTest.generates_number()'를 선택합니다.

코드가 이전 코드 스니펫과 같이 표시되면 테스트 통과입니다.

  1. 선택사항: 추가 연습을 위해 어설션을 변경하지 않고 주사위를 4면이나 5면으로 수정하여 테스트 실패를 확인합니다.

4. 축하합니다

배운 내용은 다음과 같습니다.

  • 테스트의 중요성
  • 단위 테스트의 모양
  • 단위 테스트 실행 방법
  • 일반적인 테스트 문법
  • 단위 테스트 작성 방법

자세히 알아보기