프로그래밍에는 정답이 없습니다. 더 나은 방법이 있다면 언제든 공유 부탁드립니다. Next Step 에서 제공하는 TDD-클린코드 교육 내용 기반으로 작성하였습니다.
TEST 코드를 작성해야하는 이유
개발자는 대부분의 시간을 디버깅하는 시간에 투자합니다.
사람이 많아지면 많아질 수록, 기능이 많아지면 많아질 수록 테스트해야되는 코드느 방대해지고, 연관성도 많아지게 됩니다.
작은 하나의 기능의 추가로 인해 전체의 테스트를 다시 해봐야 한다는 것은 엄청난 비용 손해입니다. 하지만, 잘 짜여진 테스트 코드만 있다면 한번만 실행 후 마음 편하게 추가할 수 있습니다.
테스트 코드의 가장 필요한 이유 중 하나는 리펙토링입니다. 리펙토링의 중요한 이유는 다들 알고 있을 것이다. 그 당시에 아무리 좋은 디자인 패턴을 짰다고 하더라도, 시간이 거듭할 수록 코드는 노후화되고 시대에 뒤쳐질 수 밖에 없습니다. 레거시를 보면서 안타깝게 느끼는 점도 그러한 이유입니다.
따라서 리펙토링을 진행해야되는데 만약 테스트코드가 존재하지 않다면, 리펙토링은 매우 힘든 작업이 된다. 리펙토링은 기존에 잘 동작하는 소스를 변경하는 작업이라, 만약 리펙토링의 사유로 기능이 동작하지 않게 된다면 그것은 어떠한 이유로라도 손해인 것입니다. 테스트든, 리펙토링이든 기능이 잘 동작한다는 것이 최우선이기 떄문입니다. 따라서 소스를 리펙토링하는 시간보다 기능 테스트 하는 시간이 더 요구된다면 그것은 손해인 셈인 것입니다. 하지만, 만약 기능을 검증해주는 테스트코드가 있다면 리펙토링을 마음 편히 할 수 있을 것이고 그러한 문화가 유지된다면 코드가 노후화 되지 않고 발전해나가는 형태가 유지될 수 있을 것입니다.
객체지향 생활 체조 원칙은 소트웍스 앤솔러지 책에서 다루고 있는 내용으로 객체지향 프로그래밍을 잘 하기 위한 9가지 원칙을 제시하고 있습니다. 이 책에서 주장하는 9가지 원칙은 다음과 같습니다.
규칙 1 : 한 메서드에 오직 한 단계의 들여쓰기(indent)만 한다. 규칙 2 : else 예약어를 쓰지 않는다. 규칙 3 : 모든 원시값과 문자열을 포장한다. 규칙 4 : 한 줄에 점을 하나만 찍는다. 규칙 5 : 줄여쓰지 않는다(축약 금지). 규칙 6 : 모든 엔티티를 작게 유지한다. 규칙 7 : 3개 이상의 인스턴스 변수를 가진 클래스를 쓰지 않는다. 규칙 8 : 일급 콜렉션을 쓴다. 규칙 9 : 게터/세터/프로퍼티를 쓰지 않는다.
단계별 개발 문화
1단계 : svn, git 등을 활용해 코드 버전 관리
1단계 : svn, git 등을 활용해 코드 버전 관리 2단계 : 이슈 관리 시스템을 통한 기능 및 일정 관리
1단계 : svn, git 등을 활용해 코드 버전 관리 2단계 : 이슈 관리 시스템을 통한 기능 및 일정 관리 3단계 : 지속적 통합 도구를 활용한 피드백 환경
1단계 : svn, git 등을 활용해 코드 버전 관리 2단계 : 이슈 관리 시스템을 통한 기능 및 일정 관리 3단계 : 지속적 통합 도구를 활용한 피드백 환경 4단계 : 코드리뷰 - 온라인 또는 짝 프로그래밍
1단계 : svn, git 등을 활용해 코드 버전 관리 2단계 : 이슈 관리 시스템을 통한 기능 및 일정 관리 3단계 : 지속적 통합 도구를 활용한 피드백 환경 4단계 : 코드리뷰 - 온라인 또는 짝 프로그래밍 5단계 : 지속적 배포
TDD란 프로그래밍 의사결정과 피드백 사이의 간극을 의식하고 이를 제어하는 기술이다. 켄트벡, Test Driven Development by Example 중 TDD의 아이러니 중 하나는 테스트 기술이 아니라는 점이다. TDD는 분석 기술이며, 설계 기술이기도 하다. 켄트벡, Test Driven Development by Example 중
TDD = TFD(Test First Development) + 리팩토링
실패하는 테스트를 구현한다.
테스트가 성공하도록 프로덕션 코드를 구현한다.
프로덕션 코드와 테스트 코드를 리팩토링한다.
테스트 주도 개발과 지속적인 리팩터링
테스트 주도 개발과 지속적인 리팩터링은 프로그래밍을 다음과 같은 대화로 바꿔, 기존 코드를 효율적으로 발전시킬수 있도록 한다
질문. 테스트를 작성함으로써 시스템에 질문한다 대답. 테스트를 통과하는 코드를 작성해 질문에 대답한다 정제. 아이디어를 통합하고, 불필요한 것은 제거하고, 모호한 것은 명확히 해서 대답을 정제한다. 반복. 다음 질문을 물어 대화를 계속한다. TDD와 지속적인 리팩터링에 대해 켄트 벡이 내건 슬로건 빨강, 초록, 리팩터링 이다.
빨강. 코드가 해야 할 일을 예상하고 이것을 나타내는 테스트를 작성한다. 테스트를 통과하는 코드를 아직 작성하지 않았기 때문에 테스트는 실패할것이다. 초록. 테스트를 통과하도록 임시방편으로라도 프로그램을 작성한다. 이 단계에서는 코드 중복, 단순함, 명확한 설계 같은 것을 고민할 필요가 없다. 그런 설계는 나중에 모든 테스트를 통과한 후 더 좋은 설계를 맘 편하게 테스트할 수 있는 단계가 되면 그 때에 가서 생각할 일 이다. 리팩터링. 테스트를 통과한 코드의 설계를 개선한다.
TDD 원칙
원칙 1 - 실패하는 단위 테스트를 작성할 때까지 프로덕션 코드(production code)를 작성하지 않는다. 원칙 2 - 컴파일은 실패하지 않으면서 실행이 실패하는 정도로만 단위 테스트를 작성한다. 원칙 3 - 현재 실패하는 테스트를 통과할 정도로만 실제 코드를 작성한다.
테스트 코드의 장단점
장점
테스트 코드가 없는 경우
사전에 검증이 끝난 부분도 재 테스트를 해야하는 사항 (전체 테스트코드가 돌아가는 모습을 보여줘야 함)
간단하게 예외처리를 해야하는 부분이나 단순한 로직을 새로 추가할 때 테스트 코드만 실행 시켜서 테스트해 볼 수 있음.
테스트코드가 없는 경우에는 다시 빌드해서 추가한 기능이 실행될 때 까지 로직을 흘려야함
단점
테스트 코드 작성에 대한 비용이 불가피함
가장 쉽게 할 수 있는 테스트의 형태가 unit test 형태인데, unit test 를 전부 할 수도 없을 뿐더러 전부 한다해서 오류가 발생하지 않는다는 보장이 없음
Intergration, E2E 테스트의 형태는 로직의 전체 흐름을 점검할 수 있는 테스트이지만 비용이 많이 요구됨
어떤 기능을 추가할 때 중심이 되는 로직은 변경이 되지 않아야 하며, ENUM 같은 인터페이스나 상속같은 중심 객체는 변경 없이 외부의 객체의 변경으로만 확장이 가능해야 합니다.
테스트 코드를 작성하는 것은 사실상 로직에 비하면 크게 어렵지 않습니다. 약간의 학습만 있다면 쉽게 구현해 낼 수 있습니다.
하지만 테스크 코드를 구현하기 위한 환경을 만드는 것은 어렵습니다. 이러한 구조의 형태는 테스트 코드를 작성하기 매우 어려워진다. 따라서 테스트코드를 구현하기 위한 환경을 만드는 것이 중요합니다. (생활 체조의 원칙 8가지)
OCP를 지킨다는 것은 SOLID 원칙의 중요한 여러가지를 지키고 있다는 것이 됩니다. 이러한 원칙은 사실상 지키기 매우 어렵습니다. 하지만 이러한 ENUM을 사용한 예시만 지킬 수 있다면 테스트코드도 매우 작성하기 쉬워지고 (ENUM의 값만 확인해주면됨) 확장성(다형성 등등) 지키기 매우 쉬워집니다.
Enum
장점
문자열과 비교해, IDE의 적극적인 지원을 받을 수 있습니다.
자동완성, 오타검증, 텍스트 리팩토링 등이 쉬워집니다.
허용 가능한 값들을 제한할 수 있습니다.
리팩토링시 변경 범위가 최소화 됩니다.
내용의 추가가 필요하더라도, Enum 코드외에 수정할 필요가 없습니다.
Java 에서 Enum 은 인터페이스를 기반으로 구성되어 있기 때문에 모든 클래스의 기능을 사용할 수 있습니다.(메서드 생성 등)
ENUM 사용 예시
새로운 Ranking이 추가되어도 profic을 가져오는 핵심로직은 변경되지 않고 Ranking을 추가해주면 됩니다. 즉, 확장에는 열려있고 변화에는 닫혀있게 됩니다.
In object-oriented programming, mock objects are simulated objects that mimic the behavior of real objects in controlled ways. A programmer typically creates a mock object to test the behavior of some other object, in much the same way that a car designer uses a crash test dummy to simulate the dynamic behavior of a human in vehicle impacts.
장점
테스트 대상 코드 격리
테스트 속도 개선
예측 불가능한 실행 요소 제거
특수한 상황 테스트 가능
감춰진 정보를 확인 가능
단점
Mock을 많이 사용해서 테스트코드를 작성하다보면 무시되어지는 로직이 많아지기 때문에 추후에 정상 동작 시 예상치 못한 문제가 발생할 수 있음
Database의 의존성을 제외한 것에 Mock을 사용해야하는 상황이란 것은 의존성 주입이 많이 주입된 상황이라는 뜻