해당 포스트는 인프런의 Java/Spring 테스트를 추가하고 싶은 개발자들의 오답노트 강의의 도움을 받았습니다
테스트 주도 개발은 RED, GREEN, BLUE 세 단계를 거칩니다.
RED 단계에서는 구현을 하지 않고 일단 컴파일 되는 코드를 작성하게 됩니다.
인터페이스를 먼저 만드는 것이 강제되며, 객체지향의 핵심원리 중 하나인 행동에 집중하게 됩니다.
BLUE 단계에서는 파괴적인 리팩토링이 일어나는데, 그럼에도 걱정이 없습니다. 이미 GREEN 단계를 거쳤기 때문입니다.
또한 새로운 기능을 추가하는 데에도 부담감이 적습니다. 테스트를 통해 기능이 적절히 추가되었는지 확인이 가능하기 때문입니다.
초기 개발 비용은 많이 들게 됩니다.
따라서 스타트업에서는 적절하지 않을 수 있습니다.
또한 함께 개발하는 개발자들의 기술 숙련도가 어느정도 있어야 합니다.
너무 적은 코드를 위해 너무 많은 테스트코드를 작성하지 않도록 주의해야 합니다.
테스트코드 실행 시간이 지나치게 길어질 수 있습니다.
테스트코드가 없는 코드를 말합니다.
지금까지 저의 모든 프로젝트가 되겠네요^^
구글에서도 불안에 떨며 릴리즈하던 시절이 있었고, 자동테스트를 도입하자 생산성이 올라갔다고 합니다 (책 "구글 엔지니어는 이렇게 일한다" 中)
불안은 개인이 철저해져야 하는 것이 아닌, 시스템의 변화가 필요한 것!
SOLID 원칙이 잘 지켜진 코드는 좋은 아키텍처를 가졌다고 볼 수 있습니다.
TEST와 SOLID는 상호보완적인 관계를 가지고 있기 때문에 테스트코드를 통해 SOLID 원칙까지 지켜낼 수 있습니다.
이러한 원칙을 잘 지키면 유지 보수가 쉬운 좋은 코드를 생산할 수 있다. → 로버트 마틴
객체 지향 프로그래밍의 5가지 핵심 원칙입니다.
테스트는 명료하고 간단하게 작성되어야 합니다.
자연스럽게 단일 책임 원칙을 지키게 됩니다.
테스트가 너무 많아져 이게 무슨 목적의 클래스인지 파악이 안 된다면, 클래스의 책임이 너무 강한 것이므로 분리가 필요합니다.
확장에는 열려 있고, 변경에는 닫혀 있어야 합니다.
테스트 컴포넌트와 프로덕션 컴포넌트를 나눠 작업합니다.
자유자재로 탈부착이 가능하게 개발하게 됩니다.
자연스럽게 개방 폐쇄의 원칙을 지키게 됩니다.
슈퍼클래스의 계약을 서브클래스가 제대로 치환하고 있어야 한다는 원칙입니다.
테스트는 모든 케이스에 대해 커버하고 있습니다.
따라서 서브클래스에 대한 치환 여부를 테스트가 알아서 판단해줄 수 있게 됩니다.
하나의 클래스는 자신이 사용하지 않는 인터페이스는 구현하지 않아야 합니다.
public interface Animal {
public String bark();
public void eat();
}
// 강아지 클래스
public class Dog implements Animal {
public String bark() {
...
}
public void eat() {
...
}
}
// 토끼 클래스
public class Rabbit implements Animal {
public String bark() {
...????????????????
}
public void eat() {
...
}
}
예를 들어 Animal이라는 인터페이스를 implements 하여 Dog와 Rabbit 이라는 클래스를 만든다고 합시다.
Dog는 Animal 인터페이스의 bark()와 eat() 모두를 구현할 수 있지만 Rabbit은 bark()를 구현할 필요가 없습니다.
그러니 bark()와 eat()은 분리되어 구현되어야 합니다.
테스트는 인터페이스를 직접 사용해 볼 수 있는 환경과 같습니다.
불필요한 의존성을 실제로 확인해 볼 수 있기 때문에 인터페이스 분리의 원칙을 지킬 수 있게 됩니다.
public class Rabbit {
private Carrot dinner;
}
예를 들어 이러한 클래스가 있을 때
Rabbit의 저녁 식사 메뉴가 Apple또한 가능해졌다고 한다면 class 내부 구현을 수정해야 합니다.
그러니 Carrot도, Apple도 가능하도록 다음과 같이 수정하면 좋겠습니다.
public class Rabbit {
private Vegetable dinner;
}
이를 간단한 클래스 다이어그램으로 나타내면 다음과 같습니다.
의존성 역전은 이후 논의할 의존성 주입과 함께 테스트코드에 적용되므로 두 개념을 구분하여 이해해야 하는 중요한 개념입니다.
구글에서 정의한 테스트 3분류입니다.
small 테스트
small 테스트는 항상 결과가 결정적(Deterministic)이고 테스트 속도가 빨라지기 때문에 중요합니다.
medium 테스트
h2와 같은 테스트 DB를 사용할 수 있습니다.
멀티 스레드 환경에서 결과가 달라질 수 있습니다.
large 테스트
small 테스트가 차지하는 비중이 가장 커야 합니다.
small 테스트에서 Blocking call이 허용되지 않는다는 말은, Non-Blocking call만 가능하다는 것일까요?
제가 생각하기에 테스트하고 싶은 로직은 대부분 DB와 결국에는 연결되어 있는데.. 어떻게 이걸 분리해서 small 테스트의 비중을 높이는지 궁금하네요!