블로그

whffkaos007

워밍업 클럽 2기 BE 클린코드&테스트 - 회고 3회

이 글은 박우빈님의 강의를 참조하여 작성한 글입니다. 벌써 3주차가 되었습니다. 읽기 좋은 코드 관련 강의가 마무리되고 실용적인 테스트 강의를 시작했습니다. 부족한 부분도 있겠지만 열심히 달려온 과정을 적겠습니다. 강의 목적테스트 코드의 중요성과 작성해야 하는 이유테스트 코드를 작성하는 방법  테스트의 필요성테스트는 기능에 대한 부가적인 요소이다. 실제로는 기능을 구현하기 바쁘다. 그런데도 왜 테스트의 중요성이 강조될까?테스트를 작성하면 위와 같은 단점이 있다. 반대로 테스트를 작성하지 않는다면 어떻게 될까? 커버할 수 없는 영역 발생경험과 감에 의존 → 인간이기에 이럼.늦은 피드백 → 수동으로 테스트 해야 함.유지보수 어려움 → 확장, 수정이 일어난다면 어디까지 영향을 미칠지 모름소프트웨어 신뢰성 낮음 → 언제 어디서 버그가 터질지 불안함  테스트 코드를 작성하지 않는다면변화가 생기는 매순간마다 발생할 수 있는 모든 Case를 고려해야 한다.변화가 생기는 매순간마다 모든 팀원이 동일한 고민을 해야 한다.빠르게 변화하는 소프트웨어의 안정성을 보장할 수 없다.테스트 코드가 병목이 된다면프로덕션 코드의 안정성을 제공하기 힘들어진다.테스트 코드 자체가 유지보수하기 어려운, 새로운 짐이 된다.잘못된 검증이 이루어질 가능성이 생긴다. 테스트 코드를 작성하지 않는다면변화가 생기는 매순간마다 발생할 수 있는 모든 Case를 고려해야 한다.변화가 생기는 매순간마다 모든 팀원이 동일한 고민을 해야 한다.빠르게 변화하는 소프트웨어의 안정성을 보장할 수 없다.테스트 코드가 병목이 된다면프로덕션 코드의 안정성을 제공하기 힘들어진다.테스트 코드 자체가 유지보수하기 어려운, 새로운 짐이 된다.잘못된 검증이 이루어질 가능성이 생긴다.   그럼 올바른 테스트 코드를 서비스에 적용시킨다면 어떤 결과를 가져올까?올바른 테스트 코드는자동화 테스트로 비교적 빠른 시간 안에 버그 발견, 수동 테스트에 드는 비용을 크게 절약소프트웨어의 빠른 변화를 지원한다.팀원들의 집단 지성을 팀 차원의 이익으로 승격시킨다.가까이 보면 느리지만, 멀리 보면 가장 빠르다.    테스트 케이스 세분화하기 테스트에는 해피 케이스와 예외 케이스가 있다.   우리는 요구사항대로 기능을 구현할 때, 이외 여러 경우를 고려해야 한다 예를 들어 커피 주문하기에 대한 기능을 구현할 때는 커피의 개수가 1 이상이여야 한다는 숨겨진 조건이 있다.이처럼 숨겨진 조건을 고려하여 작성할 때 도움이 되는 것은 경계값 테스트이다. 경계값 : 범위(이상, 이하, 초과, 미만), 구간, 날짜 등경계값을 기준으로 테스트를 고려해야 기능(도메인)에 대한 명확한 인지에 도움이 되며 예외 상황도 쉽게 파악할 수 있다.   테스트하기 어려운 영역을 분리하기테스트 코드는 작성하기 쉬운 부분과 어려운 부분이 존재한다. 예를 들어 오전 10시부터 오후 2시까지만 주문이 가능한 조건이 있다 가정하자. order(List<Item> items){ LocalDateTime now = LocalDateTime.now(); if(now < 오전 10시 || 오후 2시 < now){ // 예외 발생 } ... } 위 메서드에 대한 테스트를 진행하면 어떻게 될까?테스트를 실행하는 시간에 따라 성공 여부가 달라질 것이다.그렇다면 이처럼 테스트하기 어려운 상황이 생기면 어떻게 해야 할까?바로 테스트하기 어려운 부분을 외부로 분리해야 한다.  order(List<Item> items, LocalDateTime time){ if(time< 오전 10시 || 오후 2시 < time){ // 예외 발생 } ... } 위 코드처럼 시간이란 테스트하기 어려운 영역을 외부로 분리하여 파라미터로 받는다. 이렇게 코드를 작성하면 테스트하는 시간마다 성공 여부가 달라지지 않는다.테스트라는 기능을 온전히 수행할 수 있을 것이다. 이처럼 우리는 테스트하기 어려운 부분이 있다면 이를 외부 세계로 분리하여 테스트하기 쉽게 만들어야 한다.그럼 이런 어려운 부분은 무엇이 있을까? 어려운 영역관측할 때마다 다른 값에 의존하는 코드현재 날짜/시간, 랜덤 값, 전역 변수/함수, 사용자 입력 등외부 세계에 영향을 주는 코드표준 출력, 메시지 발송, 데이터베이스에 기록하기 등쉬운 영역 → 순수함수(pure fuction)같은 입력에는 항상 같은 결과외부 세상과 단절된 형태테스트하기 쉬운 코드이러한 영역은 위와 같이 있지만 직접 경험하면서 어떤 부분을 분리하는 것이 더 좋을 지에 대한 고민을 해야 시야를 기를 수 있다. TDD:Test Driven Development TDD는 위 구조를 통해 테스트 코드를 작성하는 것이다. 구현 → 테스트 순이 아닌테스트 → 구현 순으로 진행하는 방법이다.왜 TDD가 좋을까? 어떤 가치를 지니지?가장 큰 가치 중 하나는 빠른 피드백이다. 선 기능 구현, 후 테스트 작성테스트 자체의 누락 가능성특정 테스트 케이스만 검증할 가능성(해피 케이스)잘못된 구현을 다소 늦게 발견할 가능성테스트를 먼저 작성한다면 테스트에 대한 고려에 대한 시야를 가질 수 있다. 선 테스트 작성, 후 기능 구현복잡도 낮은 테스트 가능한 코드로 구현할 수 있게 한다.유연하며 유지보수가 쉽다.쉽게 발견하기 어려운 엣지 케이스를 놓치지 않게 해준다.구현에 대한 빠른 피드백을 받을 수 있다.과감한 리팩토링이 가능하다.  TDD : 관점의 변화지금까지 기능 구현과 테스트의 관계를 봤을 때, 우리는 아래와 같이 기능과 테스트가 상호작용하는 구조를 추구해야 한다.기존에는 테스트는 구현부의 검증을 위한 보조 수단이었다면 TDD를 이용하면 테스트와 상호 작용하며 발전하는 구현부를 가질 수 있다.클라이언트 관점에서의 피드백을 주는 Test Driven.  테스트는 문서다. 문서란?프로덕션 기능을 설명하는 테스트 코드 문서다양한 테스트 케이스를 통해 프로덕션 코드를 이해하는 시각과 관점을 보완어느 한 사람이 과거에 경험했던 고민의 결과물을 팀 차원으로 승격시켜서, 모두의 자산으로 공유할 수 있다.우리는 테스트 코드를 통해 해당 기능이 어떻게 동작하는 지 파악할 수 있다. 해피 케이스와 예외 케이스를 통해 코드(도메인 지식)를 인지하는데 도움을 얻을 수 있고 기존의 테스트를 통해 잘못된 부분이나 놓친 부분을 파악할 수 있다.우리는 항상 팀으로 일한다. 팀원에게 어떻게 보일 지 항상 고민하면서 작성하는 습관을 기르자.DisplayName을 섬세하게@DisplayName("음료 1개 추가 테스트") // 1 @DisplayName("음료 1개를 추가하면 주문 목록에 담긴다.") // 2 우리는 신규 입사자다. 서비스의 테스트 코드를 봤을 때 1, 2 중 어떤 것이 테스트 코드의 담긴 의미를 더 많이 전달해주는가?바로 2번이다. 왤까? 명사의 나열보다 문장으로 표현하기우리는 좀 더 명확한 의미를 파악할 수 있다. 정보의 생략의 없기 때문이다.테스트 행위에 대한 결과까지 기술하기이 또한 해당 기능에 대한 모든 정보를 알기 위해 행위에 대한 결과까지 기술하자.도메인 용어를 사용하여 한층 추상화된 내용을 담기(메서드 자체의 관점보다 도메인 정책 관점으로)테스트의 현상을 중점으로 기술하지 말 것  특정 시간 이전에 주문을 생성하면 실패한다. // 1 영업 시작 시간 이전에 주문을 생성하면 생성할 수 없다. // 21, 2 중 당연히 2가 더 추상화된 내용을 전달해준다. 우리가 도메인 지식을 이해하는데 더욱 도움을 준다.또한 ‘실패한다’와 ‘생성할 수 없다’를 보자. 실패한다는 것은 도메인에 대한 정보가 아니다. 단순히 테스트의 성공, 실패라는 결과에 의존한 것이다. 우리는 도메인의 정보를 전달할 수 있게 주의해야 한다.  BDD 스타일로 작성하기given - when - then → ‘테스트의 준비 - 행위 - 결과’ 를 의미한다.명확하게 표시해 줌으로써 테스트 코드를 좀 더 이해하기 쉽다.  Test의 양면성과 바라봐야 할 시각(개인 정리)이처럼 테스트에 대한 이점과 작성법이 있다. 하지만 테스트 또한 비용이다. 테스트가 오히려 기능 구현보다 비용이 비쌀 수도 있고 그렇기에 이를 단순 테스트의 용도로 바라보거나 기능 구현에 편향된 모습을 보이며 테스트 작성이 오히려 불필요하다는 반론도 많다. 하지만 우리는 지금 당장이 중요한 것이 아니다. 서비스가 운영된다면 종료되기 전까지 성장할 것이다. 또한 사용자가 많아지고 서비스의 규모가 커질수록 우리 코드는 더 많은 상호 협력을 요구하기에 결합도가 커지기 마련이다.이에 따라 장애가 발생할 확률도 높다. 테스트를 작성할 때, 항상 고민하는 습관이 있어야 장애의 발생을 예방할 수 있다. 테스트를 단순 검증의 역할로만 바라보지 말고 문서의 역할까지 크게 바라보자. 그럼에도 비용은 무시할 수 없다. 비용과 비용에서 오는 이점을 고려하여 우리는 테스트를 다루는 것도 필요한 요소라고 생각한다. 

백엔드테스트테스트필요성테스트작성법

채널톡 아이콘