TOC
참고
본 내용은 단위 테스트의 기술을 읽고 정리한 내용입니다.
책의 내용과 함께 개인적인 의견과 생각, 학습을 담아 작성하였습니다.
테스트 코드의 첫인상
첫 인상은 중요하다
- 처음일 수 있는 첫 번째 테스트가 가슴속에 오래 남길 바란다.
저의 생각
그동안 테스트 경험이 별로 없을 뿐더러, 스토리북 등 잔뜩 UI 테스트를 만들어두고 유지보수가 제대로 되지 않았어요. 이번 책을 통해 유지보수에 용이한 테스트를 만들어 가길 바라겠습니다.
테스트 구분의 중요성
- 단위 테스트와 통합 테스트는 서로 헷갈리기 쉬운 개념
- 하지만 매우 중요하기 때문에 명확히 구분해야 한다.
단위 테스트의 정의
단위 테스트(Unit Test)
정의
: 컴퓨터 프로그래밍에서 예제 코드의 특정 모듈이 의도된 대로 정확히 작동하는지 검증하는 절차의의
: 언제라도 코드 변경으로 인해 문제가 발생할 경우, 단시간 내에 파악, 수정 가능조건
: 이상적으로, 각 테스트 케이스는 서로 독립적(분리)이어야 한다.
용어 정리
SUT
- 테스트 대상(Suite Under Test)
- 일부는
CUT
로 부름(Component, Class, Code)
단위(Unit)
- 시스템 내 작업 단위(unit of work) 또는 사용 사례(use case)
작업 단위
- 눈에 띄는 결과가 나타날 때까지 발생하는 모든 작업
- 진입점의 호출부터 하나 이상의 종료점까지
진입점과 종료점
- 작업 단위에는 항상 하나의 진입점과 하나 이상의 종료점이 있다.
진입점과 종료점의 정의
진입점
- 작업 단위를 시작하는 공개(public) 함수
- 기본적인 로직을 실행하는 시작점
종료점
- 값을 반환하거나
- 어떤 상태를 변환하거나
- 서드 파티 코드를 호출하는 등 눈에 띄는 동작을 하거나
종료점마다 테스트
- 함수 호출이라는 진입점에 대해 여러 종료점이 있을 때,
- 각각의 다른 종료점에서 작업을 수행하는 곳에서 검증이 가능
- 종료점마다 테스트를 만들어 분리하라.
- 각 테스트끼리 영향을 주지 않는다.
- 더 읽기 쉽다.
- 디버깅하기 쉽다.
어떤 함수가 호출되었을 때, 함수의 종료점은 함수의 실행컨텍스트를 벗어나 다시 테스트 코드의 컨텍스트로 돌아가는 것이기 때문
쿼리와 명령
- 액션(action)은 쿼리(query)와 명령(command)으로 나뉜다.
query
: 상태 변경 없이 값만 반환command
: 상태를 변경하지만 값 반환 없음- 두 액션을 분리해서 설계하는 것이 더 낫다.(참고 - Martin Fowler)
의존성 호출
의존성
: 단위 테스트 중 온전히 제어할 수 없는 것- 서드 파티 호출
- 파일 시스템 사용
- 네트워크 통신
- 다른 팀의 코드
- DB 접근
- 오래 걸리는 계산 작업
- 의존성이 아닌 것
- 행위를 쉽게 제어 가능
- 메모리 내에서 실행
- 빠른 속도로 처리되는 경우
다른 종료점, 다른 기법
반환값이 있는 종료점(query)
- 직접 출력
- 테스트 난이도: 하
- 작업 단위의 실행 > 실행 결과 값 반환 > 반환 값 확인
상태 값을 변경하는 종료점(command)
- 간접 출력
- 테스트 난이도: 중
- A 호출 후, B를 호출해 확인하거나, A를 다시 호출하여 확인
서드 파티를 호출하는 종료점
- 테스트 난이도: 상
- 외부에 실행 주도권이 있어서 코드의 직접 간섭이 어려움
- 모의 객체(mock object) 생성 및 테스트 결과 임의 조작
좋은 단위 테스트
좋은 단위 테스트란
좋은 테스트의 특징
- 작성자의 의도를 이해하기 쉬울 것
- 읽고 쓰기 쉬울 것
- 테스트 자동화가 가능할 것
- 같은 조건에서 결과는 항상 같을 것
- 누구나 쉽게 실행할 수 있을 것
- 실패할 경우 무엇이 잘못되었는지 쉽게 알 수 있을 것
좋은 단위 테스트의 특징
- 빠르게 실행될 것
- 다른 테스트와 독립적으로 실행될 것
- 메모리 내에서 실행될 것(시스템 파일, 네트워크, DB 없이)
- 동기적인 흐름으로 실행될 것(가능한 병렬 스레드 없이)
의존성을 스텁으로 대체하기
스텁이란?
- 실제 의존성을 흉내 내는 가짜 의존성
- 실제 의존성에 접근하지 않고도 테스트 수행 가능
- 테스트가 더 예측 가능하고 안정적으로 진행
비동기 처리를 동기적 테스트로 흉내 내기
- 테스트를 작성할 때는 비동기 코드를 동기적인 방식으로 검증 가능
- 테스트에서 직접 콜백 함수를 호출하거나 비동기 작업이 완료될 때까지 대기한다는 의미
테스트의 주요 포인트
- 가독성: 읽기 쉬워야 한다.
- 유지 보수성: 배보다 배꼽이 크면 안 된다.
- 신뢰성: 테스트가 통과한다면 온전히 믿을 수 있어야 한다.
단위 테스트 체크리스트
- 2주, 2개월, 2년 전에 만든 테스트가 여전히 잘 동작하는가?
- 내가 2개월 전에 작성한 테스트를 팀 내 다른 동료가 실행했을 때 문제없이 결과를 받을 수 있는가?
- 내가 만든 테스트가 수분 내로 전부 실행되는가?
- 버튼 하나만 눌러서 내가 작성한 모든 테스트를 실행할 수 있는가?
- 기본적인 테스트를 몇 분 내로 작성할 수 있는가?
- 다른 팀 코드에 버그가 있어도 내 테스트는 통과하는가?
- 내 테스트는 다른 실행 환경에서 실행해도 동일한 결과를 보장하는가?
- 데이터베이스나 네트워크, 배포 없이도 내 테스트는 동작하는가?
- 하나의 테스트를 삭제, 이동, 변경해도 다른 테스트는 영향을 받지 않고 잘 실행되는가?
통합 테스트
- 테스트가 실제의 데이터를 사용하는 경우
- 실제 네트워크, 실제 REST API, 실제 DB
- 실제 파일 시스템, 실제 시스템 시간
- 실제 의존성을 완전히 제어할 수 없는 상태에서 작업 단위를 테스트
- 다른 팀이 만든 모듈, 외부 API나 서비스
- 네트워크, 데이터베이스, 스레드 등
- 사라진 일관성: 매번 테스트를 실행할 때마다 다른 결과
- 하지만 중요한: 시스템 전체의 상호 작용을 확인할 수 있음(단위 테스트는 불가능)
- 하지만 주의해: 단위 테스트와 분리되어야 '안전한 그린 존' 유지 가능
테스트 주도 개발(TDD)
- 실패하는 테스트를 작성
- 테스트를 통과할 수 있게 코드를 수정
- 테스트가 통과하는 것을 확인
- 리팩토링 및 이후 테스트 작성
TDD는 단위 테스트의 대체재가 아니다
- TDD는 테스트의 실패부터 시작해, 성공까지 이끄는 코드 개발을 주도한다.
- 이를 통해 테스트 자체를 검증할 수 있다.
- 이를 통해 테스트를 전적으로 믿을 수 있다.
- 다시 말하면, 실패와 통과를 모두 확인할 때 얻을 수 있는 믿음이다.
TDD를 잘하는 핵심 기법
- 좋은 테스트를 작성하는 법을 알기
- 코드보다 테스트를 먼저 작성하기
- 테스트와 프로덕션 코드를 잘 설계하기
TDD 주의사항
테스트의 선행이 좋은 테스트로 이어지는 것은 아니다
- 테스트를 먼저 작성한다고 좋은 테스트가 나오는 건 아니다.
- 그래서 좋은 테스트를 만들 줄 알아야 한다.
테스트의 가독성과 유지보수성이 TDD의 장점으로 이어지지 않는다
- 이 책은 좋은 테스트를 작성하는 방법을 알려주는 책
- 테스트를 먼저 작성하는 기술은 테스트 주도 개발을 추천
테스트의 선행과 테스트의 가독성, 유지보수성이 곧 좋은 설계는 아니다
- 코드 설계 패턴은 코드를 더 쉽게 유지 보수할 수 있도록 한다.
- 이 주제는 테스트 주도 개발로 배우는 객체 지향 설계와 실천과 클린 코드를 추천
작가의 당부
- TDD를 공부할 때에는 "단계별로 하나씩 배워 나가라"
- 한 번에 하나의 기술에 집중하고 나머지는 신경 쓰지 말아라
- 한꺼번에 익히려다가 좌절한다.
마치며
테스트의 부정적 경험과 극복
테스트 회의론을 책을 시작하기 전부터 언급했는데, 지은이도 똑같이 코드의 노후화와 함께 테스트를 통한 개발 편의성 저하를 경험을 했다고 하네요. 모두에게 처음은 있구나, 그리고 끝내 잘 해냈구나, 싶더라고요. 그렇다면 저도 이 책을 마치고 실무에 활용한다면, 테스트 회의론을 이겨내고 테스트의 효용을 느끼며 잘 활용해 볼 수 있겠다 생각했습니다.
이 책은 단위 테스트를 작성하는 방법과 이를 어떻게 유지보수하고 읽기 쉽고 신뢰할 수 있게 만드는지 알려준다고 합니다. 그 과정과 결과가 너무 기대되네요.
천 리 길도 한 걸음부터
이번 단위 테스트에 대한 학습을 마치고 실무에 적용해볼 때, 앞서 언급된 TDD를 적용해보려 했어요. 그런데, 작가의 우려대로 TDD는 좋은 테스트를 짤 줄도 알아야 하고, 단순히 테스트의 선행과 별개로 좋은 설계와 유지보수성, 가독성도 지켜져야 하는 만큼 단순한 일은 아닐 거에요.
그런데 저는 이런 경우 욕심이 과해서 한 번에 많이 병행하고, 벽에 부딪혀 자주 좌절하곤 합니다. 마치 이런 저의 성격을 알아채듯이 우려와 당부를 많은 분량으로 녹여내 표현해 주었더라고요. 그래서 작가의 당부를 잘 참고해 TDD를 적용해볼 생각입니다.
적용해볼 부분이 없었다
이번 책을 진행하며, 실제로 테스트를 실행해보고 적용해보고자 하는 목표를 가지고 있었어요. 그런데 이번 장은 overview에 가까웠어요. 앞으로 다루게 될 개념과 용어에 대한 소개와 일부 테스트 구분을 위한 서술이 대부분인 장이었거든요.
다음 장부터는 jest를 활용한 실제 테스트 코드가 소개되는 모양이니, 실제로 테스트를 실행해보고 적용해보고자 하는 목표를 달성할 수 있을 것으로 보여요.