작년부터 도입하여 사용중인 터치 시뮬레이션을 이용한 단위 테스트 소개.

의의

  • 유닛 테스팅 번들이라서 UI 테스트 마냥 앱 전체를 빌드, 설치하지 않고 원하는 모듈만 빌드하여 테스트할 수 있어서 빠르다. 테스트 하려는 화면이나 모듈 갯수를 유연하게 정할 수 있다.
  • 뷰, 비즈니스 로직, 라우팅, 서비스 계층까지 이어지는 유저 플로우 전체를 실 객체로 테스트할 수 있다. 테스트에서 주입한 mock 네트워크나 데이터 저장 계층을 가지고 최종 행위나 상태를 검증하게 됨. 테스트의 신뢰성이 높고, 내부 구현이 바뀌어도 테스트가 바뀔 경우가 많이 없음.

위험

  • Private API를 쓰고 있어서 혹시나 미래에 막힐까 하는 걱정.

계기

각 테스트의 ‘단위’를 얼만큼으로 잡을 것인가는 테스트의 속도 vs 효과성 트레이드오프를 파악하여 정해야 한다. 단위를 매우 작게 잡으면 속도는 빠르지만 실세계와 테스트 환경의 괴리가 크므로 효과성이 낮다. 단위를 크게 잡을수록 속도는 느리지만 효과성이 높다. 테스트 대상을 최대로 크게 잡은게 UI Test고, 가장 작게 잡은건 sut가 객체 하나짜리인 단위 테스트라고 볼 수 있다.

모바일 앱에서 테스트가 필요한 여러 기능 중 중요도가 높은건 사용자의 인터랙션과 복잡하게 얽힌 플로우다. 많은 버그가 사용자의 인풋을 올바르게 처리하지 못해서 발생한다. 그런데 유닛 테스트에서 사용자의 인풋을 발생시키는 방법이 마땅치 않다.

(1) control에 직접 sendActions를 호출한다.

테스트 때문에 private 접근자를 빼야하므로 싫음.

(2) 뷰를 모킹하여 사용자 인풋을 흉내낸다.

현실과 괴리감이 커지고, 경험상 구현이 바뀌면 테스트 코드도 빈번히 수정됨.

특히 2번 때문에 해결법을 찾고자 했던건데, 예전에 UI는 그대로인 상태에서 기능이 추가/수정 됐는데 테스트 코드도 많은 부분 다시 작성했던 경험을 하고나서 충격을 받았고 그때 뭔가 잘못됐다는 느낌이 왔다.

두 문제를 해결해주는게 Hammer라는 라이브러리다. RIBs로 치면 뷰, 인터랙터, 라우터를 전부 실 객체로 테스트할 수 있다는 뜻이다. 더 나아가 인터랙터가 호출하는 서비스 계층도 실 객체를 쓸 수도 있다. 모멘티 프로젝트는 서비스 계층이 의존하고 있는 플랫폼 계층(네트워킹, 데이터 저장, 엔진 등)이 한 단계 더 있어서 실 서비스 객체까지 테스트할 수 있다. Hammer를 사용하면 테스트의 인풋은 실세계와 매우 유사한 유저 터치이며 모킹된 플랫폼 계층에서 최종 행위/상태 검증을 할 수 있다. 신뢰도가 높으면서 유연하고 빠른 테스트를 만들 수 있다.