서론: 테스트되지 않은 코드의 도전 과제

구식 시스템에서 작업할 때, 코드에 충분한 유닛 테스트가 없다는 상황에 직면할 수 있습니다. 이는 변경이나 향상이 필요할 때 상당한 장애물이 될 수 있습니다. 테스트가 없으면 수정이 기존 기능을 망가뜨리지 않을 것임을 검증할 수 없습니다. 그렇다면 테스트되지 않은 코드와 테스트할 수 없는 코드를 변경하는 문제를 어떻게 해결할 수 있을까요?

이 블로그 게시물에서는 레거시 코드의 도전 과제를 탐구하고, 이를 테스트하고 리팩토링하기 위한 효과적인 전략을 제공하여 어려운 상황을 관리 가능한 작업으로 전환할 수 있도록 도와드립니다.

문제 이해하기: 왜 레거시 코드는 테스트하기 어려운가?

해결책을 다루기 전에, 일부 코드가 테스트하기 어려운 이유를 이해하는 것이 중요합니다:

  • 높은 의존성: 클래스는 종종 유닛 테스트 세트를 복잡하게 만드는 수많은 상호 의존성을 가지고 있습니다.
  • 밀접하게 결합된 코드: 관심사의 분리를 따르지 않는 코드는 테스트를 위해 기능을 분리하기 어렵게 만듭니다.
  • 안티 패턴: 좋은 소프트웨어 설계를 저해하는 관행은 테스트에 저항하는 코드를 초래할 수 있습니다.

해결책: 테스트되지 않은 코드를 테스트하기 위한 전략

1. 리팩토링으로 시작하기

리팩토링을 자주 수행하면 작업하기 어려운 코드에 대한 테스트 작성을 경감할 수 있습니다. 방법은 다음과 같습니다:

  • 가시성 수정: 프라이빗 멤버를 프로텍티드로 변경하세요. 이렇게 하면 테스트용 서브클래스를 생성하여 테스트 목적으로 메서드나 필드를 재정의할 수 있습니다.

예시

생성자에서 데이터베이스의 데이터를 초기화하는 클래스를 가정해 보세요. 이는 유닛 테스트를 거의 불가능하게 만드는 시나리오입니다.

원본 코드:

public class MyClass {
    public MyClass() {
        // 바람직하지 않은 DB 로직
    }
}

리팩토링된 코드:

public class MyClass {
    public MyClass() {
        loadFromDB();
    }

    protected void loadFromDB() {
        // 바람직하지 않은 DB 로직
    }
}

DB 로딩을 loadFromDB 메서드로 격리함으로써, 테스트 시나리오에서 이 메서드를 쉽게 재정의할 수 있게 됩니다.

2. 테스트 래퍼 생성하기

코드를 리팩토링한 후에는 테스트 래퍼를 생성할 수 있습니다:

샘플 테스트 코드

테스트 코드는 다음과 같이 보일 수 있습니다:

public class MyClassTest {
    public void testSomething() {
        MyClass myClass = new MyClassWrapper();
        // 여기에서 assert 로직
    }

    private static class MyClassWrapper extends MyClass {
        @Override
        protected void loadFromDB() {
            // 테스트를 위한 일부 목(mock) 로직
        }
    }
}

래퍼를 사용하면 목 로직을 삽입할 수 있어, 실제 데이터베이스 의존성과 테스트를 효과적으로 격리할 수 있습니다.

3. 기존 도구 고려하기

이러한 전략은 매우 효과적일 수 있지만, 현대적인 라이브러리와 도구가 테스트 프로세스를 용이하게 할 수 있다는 것을 잊지 마세요. DBUnit과 같은 프레임워크를 사용하면 데이터베이스 작업을 포함하는 시나리오를 종종 단순화할 수 있습니다.

4. 프레임워크 사용 시 주의사항

접근 수준을 변경함으로써 테스트를 위한 빠른 승리를 얻을 수 있지만, 이는 라이브러리 또는 프레임워크 저자에게 문제가 될 수 있는 내부 작업을 노출시킬 수 있습니다. 절대적으로 필요하지 않는 한 적절한 캡슐화 및 설계 원칙을 유지하도록 하세요.

결론

테스트되지 않았거나 테스트할 수 없는 코드를 테스트하고 리팩토링하는 것은 벅차게 느껴질 수 있지만, 전략적인 수정과 올바른 도구를 사용하면 레거시 시스템을 유지 관리가 가능하고 테스트 친화적인 코드로 변환할 수 있습니다. 리팩토링을 우선시하고, 테스트 래퍼를 생성하며, 사용 가능한 도구를 활용함으로써 개발자로서의 삶을 더 쉽게 만들고 소프트웨어의 회복력을 보장할 수 있습니다.

주요 요점

항상 테스트 가능한 코드 단위를 생성하려고 노력하고, 변경이 기존 기능에 미치는 영향을 염두에 두세요. 즐거운 코딩 되세요!