❗️테스트란?
기능을 개발했는데 잘 구현되었는지 어떻게 확인할 수 있을까? 바로 테스트!
기능을 개발한 후 잘 구현되었는지 테스트 코드를 작성하고 리팩토링을 통해 향상시킬 수 있다.
의도된 대로 정확히 작동하는지 검증하는 절차이다.
❗️테스트 코드 작성의 절차
소스코드가 있고, 요구사항(함수, 특정한 기능, UI, 성능, API 스펙)에 따라서 다양한 테스트가 있다.
성공 테스트와 실패테스트를 모두 작성한다
모든 테스트가 패스되면 끝이다.
만약 Fail된 데스트는 Modify or fix 해야한다.
❗️테스트 코드 작성의 장점
- 개발 과정 중 예상치 못한 문제를 미리 발견 (에러를 클라이언트 보다 빨리 발견 할 수 있다.)
- 작성한 코드가 의도한 대로 작동하는지 검증하며, 방식 및 결과 확인
- 코드 변경에 대한 사이드 이펙트를 줄이는 예방책
- 코드 수정이 필요한 상황에서 유연하고 안정적인 대응을 할 수 있게해준다.
- 코드의 모듈화를 한번더 고민하게 해준다.
- 코드 변경 시, 변경 부분으로 인한 영향도를 쉽게 파악할 수 있다.
- 리팩토링 시 기능 구현이 동일하게 되었다는 판단을 내릴 수 있다.
(예를 들어 버전업을 하면서 현재 구현된 기능이 제대로 버전업에서도 동일하게 구현되게 한다.)
테스트 코드도 추가되기 때문에 관리해야 할 코드의 양이 늘어나는 것은 맞지만 이것보다 테스트 코드를 유지함으로써 얻는 이점이 더 많다.
이것을 위해서 테스트가 항상 실패없이 실행되도록 유지해야 한다.
CI(지속적인 통합)를 통해 주기적으로 테스트 코드 유효성을 확인해야한다.
정기적인 빌드를 통해 테스트가 실패하는 케이스를 찾아낸다.
❗️테스트의 종류
테스트는 테스트 대상 범위나 성격에 따라 UI test, Integration test, Unit test 등 크게 세 가지로 구분한다.
출처ㅣ 테스트 기본 요소 - 안드로이드 개발자 공식 문서
✅ Unit Test (단위 테스트)
- 소형 테스트에 속하는 테스트이다.
- 클래스 범주 내에서 작은 단위 (함수)의 기능에 대한 유효성을 검증하는 테스트이다.
- 단위 테스트는 매우 간단하고 명확하며 빠르게 실행된다는 특징이있다.
- 하나의 함수에 대한 하나 이상의 테스트가 존재할 수 있고, 각각의 조건에 대한 유효성을 검증한다.
- 이렇게 작성된 테스트가 많을수록 해당 로직에 대한 신뢰도가 높아질 수 있다.
- 또한, 작게 쪼개진 단위 테스트는 해당 로직이 어떤 역할을 하는지 쉽게 파악할 수 있다.
✅ Integration Test (통합 테스트)
- 중형 테스트에 속하는 테스트이다.
- 서로 다른 모듈 혹은 클래스 간 상호작용의 유효성을 검사하는 테스트이다.
- 이러한 통합 테스트가 필요한 이유는 각각 단위 테스트가 검증되었다 하더라도, 모듈 간 인터페이스 및 데이터 흐름이 의도한 대로 작동하지 않는 경우도 있기 때문이다.
그래서 조금 더 넓은 범위에서 추가적인 테스트가 필요하다.
- 또한 통합 테스트는 각 모듈에 대한 설정 또는 테스트를 하기 위해 사전 조건이 필요한 경우도 있기 때문이다.
- 단위 테스트보다 테스트 코드를 작성하기가 복잡하다.
- 하지만 단위 테스트 보다 더 넓은 범위의 종속성까지 테스트함으로써 단위 테스트 보다 좀 더 유의미한 테스트가 되는 경우가 많다.
✅ UI Test
- 대형 테스트로 분류되는 테스트이다.
- 실제 사용자들이 사용하는 화면에 대한 테스트를 하여 서비스의 기능이 정상적으로 작동하는지 검증하는 테스트이다.
- UI 테스트는 실제 앱을 사용하는 사용자의 흐름에 대해 테스트 함으로써 UI 변경 사항으로 발생할 수 있는 문제를 사전에 차단한다.
- 그렇게 함으로 신뢰성을 높인다.
- 하지만 화면과 직접적으로 연관되어있는 테스트이다보닌 실행 시간도 오래 걸리고 디자인이 변경될 때마다 테스트 코드의 수정이 필요하다.
- 그래서 유지보수 비용도 크다.
UI 테스트, 조금 더 살펴보기
다음은 로그인 하는 기능의 ui테스트다.
UI테스트의 코드 구현은 직관적인 편이다.
위 샘플코드의 구성은
2~3 라인: 테스트할 화면으로 이동하여 테스트 준비
5~9 라인: 유저가 수행할 동작을 코드로 수행
11~13 라인: UI동작 결과 검증
개인적으로 given-when-then 와 함께 기능의 동작 시나리오를 함께 고려하며 테스트 코드 작성을 추천!
❗️ 테스트 피라미드
테스트 피라미드는 테스트하고자하는 단위와 목적에 따라 테스트를 구분하고 있다.
단위 테스트는 가장 작은 단위인 함수를 검증하고, 통합 테스트는 모듈 간 상호작용이 올바르게 동작하는지 검증하고, UI 테스트는 가장 마지막에 사용자 인터랙션이 오류없이 동작하는지 검증한다.
피라미드 형태로 표현된 이유는 테스트 코드를 작성하는 데 필요한 비용도 다르기 때문이다.
단위 테스트는 검증하려는 범위가 작기 때문에 작성이 수월하고, 검증 범위도 작기 때문에 무수히 많은 테스트가 작성될 수 있다.
통합 테스트는 좀 더 넓은 범위의 검증이기 때문에 고려할 부분이 많아서 더 많은 비용이 필요하다.
UI 테스트는 가장 많은 비용과 시간이 필요하지만 사용자 시나리오 기반으로 작성하기 때문에 테스트 코드의 양은 적다.
구글 공식 문서에서는 각 테스트 비율을 단위는 70%, 통합은 20%, UI은 10%로 구성하는 것이 좋다고한다.
테스트를 처음 도입하는 분이라면 비교적 작성의 비용이 적은 단위 테스트부터 시작하는 것을 추천한다고 한다.
❗️ 테스트 코드 작성이 어려운 이유
시간 부족이다.
이유는 테스트 코드 작성을 고려하지 않은 개발 일정을 산정했기 때문이다.
이러한 문제를 해결하기 위해서는 작업 일정을 산정할 때 테스트 코드를 작성하기 위한 기간을 별도로 계획하는 노력이 필요하다.
이렇게 하기 위해서는 함께 일하는 팀원들이 테스트 코드의 필요성에 대해 공감하고 있어야한다.
❗️ TDD(테스트 주도 개발)란?
TDD란 Test Driven Development의 약자로 '테스트 주도 개발'이라고 한다.
- Test-driven development: 개발(코드 작성)전 테스트 코드를 먼저 작성하는 것이다.
- 개발 방식, 개발 방법론 중 하나이다.
- 테스트를 먼저 짜고 테스트를 통과할만큼의 코드만 짜서 테스트를 한다.
- 실패하는 테스트를 먼저 작성하고 그에 해당하는 기능을 만들어나가는 방식이다.
- 이후엔 리팩토링으로 효율성 향상시켜야한다.
✅ TDD 특징
- 케이스 단위로 테스트 정리: 기능을 구현하는데 절차
- 예외 케이스를 생각해야 한다.
예) 잘못된 경로를 줬을 경우? 파일용량이 큰 경우? 다양한 브라우저?
케이스 별로 실제 행위 유발: 직접적으로 함수를 불러 호출을 하거나 커맨드 라인등을 통해 간접 호출 하는 것이 좋다.
- 행위의 결과 확인: pass/fail
- 케이스 자동화
✅ TDD 좋은 점
- 요구사항에 대한 분석 및 이해를 바탕으로 설계자의 관점에서 개발할 수 있다.
- 모든 요구사항, 목표에 대해서 점검할 수 있고, 사용자 입장에서 코드를 작성할 수 있다.
(시스템 전반적인 설계 향상)
- 자연스럽게 테스트 통과율이 높아진다.
- 오버 엔지니어링 방지
- 설계에 대한 피드백에 빠르다.
❗️결론
앞으로 테스트 코드 작성하는 시간까지 계산해서 프로젝트 개발 소요 시간을 계획해야한다.
그러기 위해서 개발자뿐만 아니라 모두가 테스트 코드 작성의 중요성에 대해
서로 이해하고 필요성을 공유하고 있어야한다.