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