본문 바로가기
Android/Test

Android Test - 1. Basic

by 안스 인민군 2024. 3. 31.

이번 글은 TestCode 도입에 앞서 지식에 대해서 정의하려고 한다.

 

The History of Software Testing에서는 소프트웨어 테스팅이 1822년 찰스 베비지의 차분 엔진 제작과 함께 시작되었다고 하고 Bug라는 단어는 토마스 에디슨이 1878년 동료에게 보내는 편지에서 처음 사용된 것으로 알려져 있다. 따라서 테스트 방법론에서는 서로 대립하는 수많은 주장이 긴 시간동안 개발되어져 왔다.

 

테스트 유형이 피라미드 구조를 가져야 한다는 사람도 있고 아이스크림 콘 형태를 가져야한다는 사람도 있다. 또 테스트

커버리지는 100%를 목표해야 한다는 사람도 있고 혹은 100%를 목표하는 것은 무의미한 일이라고 말하기도 한다. 또 테스트를 먼저할지 구현이 먼저되어야 하는지에 대한 의견이 분분하다. 모든 주장은 나름의 논리가 있기에 무엇이 옳고 그런지 알 수 없다.

그래서 이 파트에서는 방법론 보다는 기술적으로 안드로이드에서 코드 테스트를 어떻게 수행하는지에 대해 중점을 두고 설명한다.

 

테스트 자동화의 필요성

테스트 유형

  • 기능 테스트 : 내 앱이 기능을 수행하는지
  • 성능 테스트 : 빠르고 효율적으로 수행하는지
  • 접근성 테스트 : 접근성 서비스와 잘 작동하는지
  • 호환성 테스트 : 모든 기기와 API 수준에서 잘 작동하는지

위와 같은 테스트를 전문 QA 팀을 운영하거나 개발자가 일일히 확인하는 수동 테스트로 테스트를 하는것은 빌드시간도 증가하고 비용이 너무 많이 증가하게 된다. 그렇기에 우리는 자동화 테스트를 통해 일일히 빌드할 필요가 없고 개발시간이 감소하고 비용이 절감하고 테스트를 고려한 개발로 짜기 때문에 견고한 구조를 가지게 된다.

자동 테스트의 분류

테스터 입장에서의 분류

  • 블랙박스 테스트 : 소프트웨어의 내부구조나 작동원리를 모르는 상태에서 소프트웨어의 동작이 요구사항을 만족하는지 검사하는 방법으로 개발자가 아닌 사용자 입장에서의 테스트
  • 화이트 박스 테스트 : 블랙박스 테스트와 반대로 소프트웨어의 내부구조와 제어 흐름을 직접 관찰하는 테스트로 개발자 입장에서의 테스트

구글의 분류

안드로이드 테스트 자동화에는 블랙박스 테스트와 화이트 박스 테스트 모두 사용하게된다.

구글은 아래 그림과 같이 Unit Test, Integration Test, End-To-End Test 로 나뉘게 되는데 바깥으로 갈 수록 각 테스트의 충실도는 증가하지만 실행시간과 유지보수 및 디버깅에 드는 노력도 증가하게 된다. 따라서 일반적으로는 Unit Test를 Integration Test보다 많이 작성하고  Integration Test를 End-To-End Test보다 더 많이 작성하게 된다.

Unit Test(단위 테스트)

앱의 메서드 또는 클래스와 같은 작은 단위의 기능을 검증한다.

로컬 단위 테스트 (Local Unit Test)

  • Android Framework에 의존하지 않고 로컬 JVM에서 실행되는 테스트
  • 실제 기기나 에뮬레이터를 사용하지 않으므로 테스트 속도가 빠름

계측 단위 테스트 (Instrumented Unit Test)

  • Android Framework에 의존하는 기능을 검증하는 단위 테스트
  • 실제 기기 또는 에뮬레이터에서 실행해야 하기 때문에 실행시간이 오래 걸림

Intergration Test(통합 테스트)

서로 다른 모듈 또는 클래스 간의 상호작용이 정상적으로 기능하는지를 검증

End-to-end Test(종단간 테스트)

앱의 전체 화면 또는 여러 모듈에 걸친 사용자 흐름과 같은 큰 부분에 대한 기능 검증을 수행

UI 테스트라 부르기도 함

테스트 사이즈

위의 세가지의 테스트는 이름이 모호하게 들릴 수 있기에 구글에서는 단위 테스트를 Small, 통합 테스트는 Medium, 동단간 테스트를 Large라고 부른다.

크기에 따른 특징은 아래 표와 같다.

테스트 기본 원칙

테스트할 때 지켜야 되는 몇 가지 원칙을 소개하겠다. 

Seven Testing Principles

Seven Testing Principles는 인터내셔널 소프트웨어 테스팅퀄리피케이션 모드에서 제안한 7가지 테스트 원칙이다.

  • Testing shows the presence of defects, not their absence : 테스팅은 결함의 존재를 보여주는 것이다.
  • Exhaustive testing is impossible : 완벽한 테스트는 불가능하다.
  • Early testing saves time and money : 테스트 구성은 가능한 빠르게 시작한다.
  • Defects cluster together : 결함은 군집되어 있다.
  • Beware of the pesticide paradox : 살충제 역설 - 비슷한 테스트가 반복되면 새로운 결함을 발견할 수 없다.
  • Testing is context dependent : 테스팅은 문맥에 의존적이다.
  • Absence-of-errors is a fallacy : 오류 부재의 궤변 - 사용되지 않는 시스템이나 사용자의 기대에 부응하지 않는 기능의 결 함을 찾고 수정하는 것은 의미가 없다.

F.I.R.S.T Principles

F.I.R.S.T Principles '로버트 마틴' 선생의 클린 코드에서 소개한 단위 테스트에 관한 원칙들을 '브랫과 팀' 이라는 사람이 F.I.R.S.T 라는 이름으로 정리한 것이다.

  • Fast : 단위 테스트는 빨라야 한다.
  • Isolated : 테스트가 다른 테스트 케이스에 의존하지 않아야 한다.
  • Repeatable : 테스트는 실행할 때마다 같은 결과를 만들어야 한다.
  • Self-validating : 테스트 결과는 성공이거나 실패여야 한다. 결과에 대한 해석이 필요해서는 안된다.
  • Timely : 단위 테스트는 기능이 출시된 후에도 언제든 작성할 수 있지만 적절한 시점은 프로덕션 코드를 구현하고 있는 와 중에 테스트 코드를 작성하는 것이다.

테스트 코드 작성 스타일

위의 내용에서 테스트의 원칙은 이해했을 것이다. 그러면 테스트 코드는 어떤 스타일로 작성해야 될까?

Given-When-Then 스타일

넓은 공감대를 얻고 있는 스타일 중에 하나는 마틴 파울러 선생이 제안한 Given-When-Then 스타일이다.

이 GWT는 테스트 코드 구조를 다음 세 부분으로 분리해서 생각한다.

  • Given : 어떠한 상태하에서
  • When : 어떠한 기능을 실행하면
  • Then : 어떠한 결과가 나와야 한다.

Test Double

영화 촬영에서 우리가 흔히 아는 스턴트맨의 정확한 용어는 스턴트 더블이라고 하는데 테스트 더블은 말 그대로 실제 구성요소 대신 테스트를 수행하는 객체이다

  • 실제 구성요소 대신 테스트를 수행하는 객체
  • xUnit Test Patterns의 저자인 제라드 메스자로스(Gerard Meszaros)가 만든 용어
  • F.I.R.S.T Principles의 ` Isolated ` > 테스트 대상이 의존하는 것을 실제가 아닌 다른 것으로 대체

 

1. Dummy : 인스턴스는 필요하지만 기능까지는 필요하지 않은 경우에 사용한다.

2. Stub : 인터페이스 혹은 클래스를 최소한으로 구현하고, 결과는 특정 상태를 가정해서 하드코딩된 값을 제공하며 상태 기반 테스트(State-based testing)에 사용한다.

3. Spy : Stub과 비슷한 객체인데 추가적인 정보를 더 기록할 수 있게 구성한다. 어떤 메소드 A가 호출 되었을 때 또 다른 메소드 B가 실행이 되었는지 확인하는 등의 행위 기반 테스트 (Interaction-based testing)에 사용한다.

4. Fake : 동작하는 구현이 있기에 테스트에는 사용할 수 있지만 프로덕션 구현과 동일하지는 않은 객체이다.

5. Mock : Spy은 메소드 A와 B의 실제 동작을 추적하지만 Mock은 A가 실행되면 그냥 B의 정상 작동값을 반환하도록 구성

테스트 대상 항목

구글에서 제안하는 앱의 주된 테스트 대상 항목은 다음과 같다.

  • 단위 테스트 대상
    • ViewModels 또는 Presenter
    • 데이터 레이어
    • 유즈 케이스
    • 값을 계산하는 유틸리티 클래스
  • 엣지 케이스
    • 음수, 0 및 경계 조건 을 사용하는 수학 연산
    • 가능한 모든 네트워크 연결 오류
    • 형식이 잘못된 JSON과 같은 손상된 데이터
    • 파일 저장시 스토리지가 가득 찬 상황
    • 프로세스 중간에 다시 생성된 개체(예: 장치가 회전할 때의 액티비티)
  • UI 테스트
    • 스크린 UI
    • 유저 플로우
    • 내비게이션
  • 테스트 제외 대상
    • 프레임워크 자체의 동작
    • activities, fragments, 혹은 services에는 테스트가 필요한 로직을 가능한 배치하지 않음

테스트 라이브러리

  • JUnit4 : Java의 단위 테스트 코드를 작성하기 위해 만들어진 프레임워크로 Jetpack Test 라이브러리는 JUnit4를 기준으로 만들어져 있음. 현재 최신 버전은 JUnit5이나, 안드로이드를 완벽히 지원하지 않음.
  • Kotest : Kotlin의 단위테스트 코드를 작성하기 위해 만들어진 프레임워크
  • Roboletric  : JVM 만으로 안드로이드 프레임워크를 테스팅하기 위해 만들어진 프레임워크.

Assertion

테스트를 할 땐 이것이 테스트된 값이 맞는지 안 맞는지 Assertion을 해야 되는데 JUnit 그 자체로는 표현력이 부족하기 때문에 이 표현력이 부족한 단점을 메워주는 라이브러리들이 있다.

  • AssertJ
  • Truth
  • Hamcrest

Mocking

  • Mockito : Java 테스트를 위한 Mocking 라이브러리
  • MockK : Kotlin 테스트를 위한 Mocking 라이브러리

UI Testing

  • Espresso : 단일 안드로이드 앱의 UI를 테스트하는 프레임워크
  • Kaspresso : KasperskyLab에서 제작한 안드로이드 앱 UI 테스트 프레임워크
  • Appium : iOS와 Android를 모두 테스트 할 수 있는 UI 테스트 프레임워크
  • UI Automator : 복수 앱 간의 UI 기능을 테스트하는 프레임워크

구글의 안드로이드 테스트 환경

구글은 테스팅의 중요성을 지속적으로 강조해 왔고 테스팅 전략이나 새로운 테스팅 라이브러리를 꾸준히 발표해 왔다.

이것들은 구글 I/O 에서 발표된 테스팅에 관련된 동영상들 이다. 계속 꾸준히 이렇게 발표해 오고 있다는 것을 알 수가 있다

  • Android Testing (Android Dev Summit 2015)
  • Test-driven development on Android with the Android Testing Support Library (Google I/O '17)
  • Frictionless Android testing: Write once, run everywhere (Google I/O '18
  • Testing rebooted (with AndroidX Test) (Android Dev Summit '18)
  • Testing Android apps at scale with Nitrogen (Android Dev Summit '18)
  • Build testable apps for Android (Google I/O’19)
  • What’s new in Android testing tools | Session

Jetpack Test Library : 안드로이드 모듈의 테스트를 위한 핵심 라이브러리가 바로 Jetpack 테스트 라이브러리이다.

UI/Application Exerciser Monkey : Monkey는 에뮬레이터 또는 장치에서 실행되고 클릭, 터치 또는 제스처와 같은 사용자 이벤트의 의사 무작위 스트림과 여러 시스템 수준 이벤트를 생성 하며 여러가지 옵션을 지정 가능

Unified Test Platform : 서로 다른 OS의 테스트를 모듈 방식으로 추가할 수 있고 병렬로 테스트를 수행할 수 있어서 테스트의 대규모화가 가능

Code Coverage

내 코드가 얼마만큼 테스트 되고 있는지 평가할 수 있는 지표이다.

Java Code Coverage Library(JaCoCo)

여기에 Code Coverage 가 100% 를 하기 위해서만 노력할 필요없다.

드리븐 디벨로먼트의 저자인 켄트백 선생은 코드 커버리지 100%를 뽐내는 사람은 신문에 글자 하나하나를 읽었다고 자랑하는 것과 같다고 비판하기도 했다. 어쨌든 코드 커버리지를 높이기 위해서는 더 많은 기능에 대한 테스트를 만들어야 한다. 그러려면 각 기능이 테스트를 하기 좋은 구조로 되어 있어야 한다. 그렇다면 테스트를 하기 좋은 코드라는 건 어떤 것일까?

이 부분은 간단히 설명할 수 있는 주제가 아니기 때문에 대신 인사이트를 얻을 수 있는 훌륭한 세미나 발표를 하나 소개해 드리도록 하겠다.

OKKY가 TDD를 주제로 2018년에 주최한 OKKYCON 2018 더 리얼 TDD라는 세미나가 있는데 이 당시 발표 자료가 유튜브에도 공개가 되어있기 때문에 참조하시면 좋을 것 같다.

CI/CD

프로덕트의 빌드 과정은 아래와 같다. 이러한 과정은 서비스가 크면 복잡해질 뿐 아니라 변경이 10분마다 한 번씩 일어난다면 빌드를 수행하다가 하루가 끝나버린다. 또 만약에 수십 명의 개발자가 기능을 개발하는 데 연관되어 있다면 이제는 어떤 순서로 빌드를 수행해야 할지도 알 수 없게 돼 버릴 것 이다. 

  1. 연관된 의존성 다운로드
  2. 소스 코드를 바이너리 코드로 컴파일
  3. 바이너리 코드 링크하여 실행가능 파일로 패키징
  4. 테스트 수행
  5. 프로덕션 시스템에 배포

  • CI/CD 지속적 통합(Continuous Integration, CI) : 개발자가 코드를 작성하고 저장소에 커밋하면 자동으로 컴파일과 테스트를 수행
  • 지속적 제공(Continuous delivery, CD) : CI가 완료되어 배포할 수 있는 상태의 빌드 결과물을 저장소에 업로드 하는 것
  • 지속적 배포(Continuous Deployment, CD) : 지속적 제공 과정이 완료된 결과물을 최종 사용자가 사용할 수 있는 환경까지 자동으로 배포하는 솔루션

 


참고

- 냉동코더의 알기 쉬운 Modern Android Development 입문 (Link)

'Android > Test' 카테고리의 다른 글

Android Test - 3. Instrumented Test  (0) 2024.03.31
Android Test - 2. Local Test  (0) 2024.03.31