메서드가 한 가지 기능만 수행하는지 확인하는 기준을 세우면 코드의 품질을 높이고 유지보수를 더 쉽게 할 수 있다.
중복 코드 확인:
메서드의 길이 제한:
메서드 이름을 통해 역할 파악하기:
하나의 책임 원칙:
의미 있는 구간으로 분리 가능 여부:
public class OrderProcessor {
public void processOrder(Order order) {
// 유효성 검사
if (order == null || order.getItems().isEmpty()) {
System.out.println("Invalid order.");
return;
}
// 가격 계산
double total = 0;
for (Item item : order.getItems()) {
total += item.getPrice() * item.getQuantity();
}
// 할인 적용
if (order.isDiscountApplicable()) {
total *= 0.9; // 10% 할인
}
System.out.println("Total order cost: " + total);
}
}
위 코드는 여러 기능을 포함하고 있으며, 중복된 검증 코드도 존재한다. 이를 기능별로 분리하고, 유효성 검사와 계산 작업을 각각의 메서드로 나눠보자.
public class OrderProcessor {
private static final double DISCOUNT_RATE = 0.9;
public void processOrder(Order order) {
if (!isValidOrder(order)) {
displayInvalidOrderMessage();
return;
}
double total = calculateTotalPrice(order);
total = applyDiscountIfApplicable(order, total);
displayTotalPrice(total);
}
// 1. 유효성 검사 메서드
private boolean isValidOrder(Order order) {
return order != null && !order.getItems().isEmpty();
}
// 2. 유효하지 않은 주문 메시지 출력 메서드
private void displayInvalidOrderMessage() {
System.out.println("Invalid order.");
}
// 3. 총 가격 계산 메서드
private double calculateTotalPrice(Order order) {
double total = 0;
for (Item item : order.getItems()) {
total += item.getPrice() * item.getQuantity();
}
return total;
}
// 4. 할인 적용 메서드
private double applyDiscountIfApplicable(Order order, double total) {
return order.isDiscountApplicable() ? total * DISCOUNT_RATE : total;
}
// 5. 총 가격 출력 메서드
private void displayTotalPrice(double total) {
System.out.println("Total order cost: " + total);
}
}
isValidOrder로 분리되어 재사용이 가능하다.processOrder 메서드는 주문 처리의 전체 흐름을 담당하고, 실제 작업은 각 기능별 메서드에 위임하여 단일 책임을 유지한다.외부 상태나 의존성을 요구하지 않는지 확인
메서드나 클래스가 특정 외부 상태(파일, 데이터베이스, 네트워크)에 의존하지 않고 독립적으로 동작할 수 있어야 한다.
예를 들어, 계산 메서드가 외부 데이터베이스 연결이나 파일 시스템 상태에 의존하지 않으면 독립적으로 테스트하기 쉽다.
필요한 경우, 의존성을 외부로 분리하여 의존성 주입(DI)이나 Mocking 기법을 사용하여 독립성을 높일 수 있다.
입력값만으로 테스트 가능 여부 확인
메서드나 클래스가 입력값을 기반으로 출력을 결정할 수 있다면 독립적으로 테스트하기 쉬운 구조이다.
예를 들어, calculateTotalPrice와 같은 메서드는 입력으로 주어진 Order 객체만으로 계산 결과를 반환하기 때문에 외부 상태에 영향을 받지 않는다. 이는 독립적인 테스트가 가능한 메서드이다.
변경할 필요 없는 외부 객체에 의존하지 않는지 확인
테스트 대상 메서드가 외부 객체에 의존하지 않거나, 의존하더라도 해당 객체를 변경할 필요가 없는 경우 독립적인 테스트가 가능하다.
외부 객체의 상태를 조작해야만 테스트가 가능한 경우, 이는 독립적인 테스트가 어렵다는 신호이다. 이런 경우 Mock 객체를 사용하거나 해당 객체를 분리하는 방법을 고려할 수 있다.
단순 출력이나 계산을 위한 메서드인지 확인
단순히 계산이나 변환을 수행하는 메서드는 외부 상태에 영향을 주지 않기 때문에 독립적으로 테스트하기 쉽다.
예를 들어, applyDiscountIfApplicable 메서드는 할인율을 계산하여 반환하는 역할만 하므로, 독립적인 입력값에 따라 결과를 테스트할 수 있다.
의존성 주입을 통한 외부 의존성 분리 여부 확인
외부 종속성이 필요한 경우, 의존성 주입(DI)을 사용하여 테스트 시 쉽게 대체할 수 있는 구조로 만들면 독립적인 테스트가 가능하다.
예를 들어, 데이터베이스 연결이 필요한 클래스는 DB 연결 객체를 외부에서 주입받도록 하면, 테스트 시에 Mock DB를 주입하여 독립적으로 테스트할 수 있다.
단위 테스트 작성 시 쉽게 검증할 수 있는지 확인
단위 테스트를 작성해보면서 독립적인 테스트가 가능한지 확인할 수 있습니다. 테스트가 잘 동작하지 않거나 외부 설정이 필요하다면 독립성이 부족하다는 신호이다.
예를 들어, 테스트 코드에서 다른 클래스나 외부 리소스를 설정하지 않고, 단일 메서드만 테스트할 수 있는지 확인해야한다.