이번 게시글에서는 본격적인 안드로이드에서의 테스트를 다루기 이전에, 테스트란 무엇이고 왜 하는 지, 안드로이드에서 테스트는 어떤 것들이 있는 지를 살펴볼 것이다. 또한 이러한 테스트를 잘 하기 위해서는 어떠한 방법으로 테스트를 설계하는 것이 좋을 것인지에 대한 일반적인 방법론도 살짝 살펴볼 것이다.
본 내용은 Advanced Android with Kotlin - Testing Basics의 내용 중 일부를 해석한 것이다.
프로덕트를 개발하는 과정에 있어서 테스트는 굉장히 중요하다. 나의 앱이 잘 돌아"갈 것이다"와 나의 앱이 잘 돌아"간다"라는 차이에서 오는 감각이 이를 증명해준다. 테스트는 버그나 크래쉬에 대해서 큰 실마리가 되어줄뿐만 아니라 리팩토링을 할 때 테스트가 가능한 코드, 즉 이미 최적화가 되어진 코드를 제공해주어 개발 속도에 큰 차질을 빚지 않게 해준다. 또한 테스트는 코드 그 자체를 설명해주는 잘 정리된 문서 역할을 한다.
One of the many benefits of testing is that your tests act as a kind of documentation of what your code does.
안드로이드에서 테스트는 크게 두 개로 구분지을 수 있다.
JVM, 즉 코틀린 코드로 충분히 수행할 수 있는 테스트를 의미하며, 비즈니스 로직(혹은 그냥 로직)이 정확히 도는 지에 대한 검증을 수행할 수 있다.
에뮬레이터나 실기기에서 수행할 수 있는 테스트의 일종이며 이 종류의 테스트를 수행할 시 에뮬레이터에서 테스트를 수행하는 것을 볼 수 있다. 따라서 로컬 테스트보다 보기 쉽고 로컬 테스트에서 수행하지 못하는 점들을 캐치하여 검증을 할 수 있지만 실기기를 이용하는 만큼 로컬테스트보다 테스트 속도가 느린 것이 단점이다.
구현 목표를 테스트 코드를 통해 미리 설계를 해놓고 테스트의 결과가 참이 되게 구현을 해 나가는 방식을 의미한다. TDD의 결과를 결과가 검증된 프로덕트가 나오니 높은 퀄리티의 프로덕트가 나옴을 보장한다. 여기서 높은 퀄리티의 프로덕트란?
위에서 말했듯 테스트는 그 코드를 설명하는 하나의 문서이기도 하니, 테스트 코드를 작성한다면 잘 작성해야할 필요가 있다. 이를 용이하게 하기 위한 방법들은 다음과 같다.
테스트의 이름을 잘 지어라
테스트의 목적_로직이나 입력값_결과상태
ex) getActiveAndCompletedStats_noCompleted_returnsZeroHundred
3개의 단계로 구분지어 테스트를 구조화 시켜라 Given/When/Then
Assertion Framework를 잘 사용해라
// JUnit
assertEquals(result.completedTasksPercent, 0f)
// Assertion Framework(hamcrest, truth)
assertThat(result.completedTasksPercent, `is`(0f))
결국 테스트 코드는 우리가 읽기 쉽게 적어야 하기에 이런 유틸들을 가져와서 사용하는 것을 권장한다.
앱을 테스트를 할 때 어느정도로 테스트를 할 것인지도 중요한데, 그 범위는 세가지로 정리할 수 있다.
여러가지의 클래스로 이뤄진 하나의 기능을 테스트할 때
여러가지의 클래스가 맞물려지는 형태에 대한 검증
상황에 따라서 Instrument 테스트, 로컬 테스트 모두 돌아갈 수 있음
만약에 Activity에 모든 코드를 다 때려박았다면? 액티비티를 테스트 할 때, End-to-End의 의미에서의 테스트만 가능하지 Unit, Integration Test는 사실상 불가능하다. 그래서 기능을 아키텍처로 쪼개는 것이다.
Unit Test를 시행하기 좋은 곳
Integration Test
그렇다면 내 앱이 테스트가 불가능한 이 큰 문제인가요?
A) 아 그건 아님, TDD에서 궁극적인 목적은 결국 버그가 없는 프로덕트이기에 테스트가 없는 리팩토링 보다는 테스트 코드를 하나라도 작성하는 것이 유의미한 검증을 하는 것이다