원문: https://testing.googleblog.com/2008/06/defeat-static-cling.html (Translated by Google Gemini)


당신은 페어 프로그래밍을 하고 있고, 많은 뛰어난 사람들이 그렇듯이 소리 내어 말하고 있습니다. “목을 만들고, 주입하고, 테스트를 다시 실행할 거야. 통과해야 하는데… 젠장!” 당신의 파트너는 예외 "ConnectionFactory not initialized"를 발견합니다. “뭐?” 그녀는 말합니다. “뭔가가 데이터베이스를 사용하고 있어? 젠장, 이건 작은 테스트여야 했는데.”

검사해보니 당신의 클래스가 다른 클래스의 정적 메서드를 호출하고 있다는 것을 발견했습니다. Static Cling1이 발생한 것입니다! 만약 정적 메서드에 의존하는 코드를 생성하는 데이터 지속성 레이어를 (잘못) 사용하고, 주의를 기울이지 않았다면, 당신의 코드는 다음과 같을 수 있습니다.

public class MyObject {
  public int doSomething(int id) {
    return TheirEntity.selectById(id).getSomething();
  }
}

결과적으로, TheirEntity의 정적 메서드를 호출하지 않고는 doSomething을 호출할 수 없습니다. 이 코드는 자바에서 정적 메서드를 모의하기 어렵기 때문에 테스트하기 어렵습니다.

그렇다면, 이러한 형태의 Static Cling을 제거하고 작은 테스트를 통과시키려면 어떻게 해야 할까요? 때로는 Repository Pattern이라고 알려진, Abstract Factory의 한 형태인 기술을 사용할 수 있습니다. 모의할 수 없는 정적 메서드 호출을 포함하는 인터페이스와 구현을 생성합니다.

interface TheirEntityRepository {
  TheirEntity selectById(int id);
  // TheirEntity의 다른 정적 메서드도
  // 여기에 나타낼 수 있습니다.
}

public class TheirEntityStaticRepository
        implements TheirEntityRepository {
  public TheirEntity selectById(int id) { // 비정적
    return TheirEntity.selectById(id); // 정적 메서드 호출
  }
}

다음으로, MyObjectTheirEntityRepository를 주입하고 정적 메서드 호출 대신 사용합니다.

public class MyObject {
  private final TheirEntityRepository repository;

  public MyEntity(TheirEntityRepository arg) {
    this.repository = arg;
  }

  public int doSomething(int id) {
    return repository.selectById(id).getSomething();
  }
}

TheirEntity의 소스 코드에 접근할 수 없어도 이 작업을 수행할 수 있습니다. 소스 자체를 변경하는 것이 아니라, 단순히 해당 정적 메서드를 주입 가능한 인터페이스 안에 캡슐화하는 것이기 때문입니다. 여기에 제시된 기술은 정적 메서드가 객체의 팩토리 역할을 하는 경우에도 일반화됩니다.

이제 “아무것도 찾지 못하는”, “항상 예외를 던지는”, “ID가 소수인 경우에만 TheirEntity를 반환하는” 등 다양한 테스트를 위해 리포지토리의 다른 구현을 주입할 수 있습니다. 이러한 종류의 테스트는 이 리팩토링 전에는 불가능했을 것입니다.


  1. 정적(static) 멤버에 너무 강하게 들러붙어(의존하여) 특히 단위 테스트(Unit Test)를 어렵게 만들고 코드의 유연성을 떨어뜨리는 안티패턴 ↩︎