원문: https://testing.googleblog.com/2013/06/testing-on-toilet-fake-your-way-to.html (Translated by Google Gemini)


수년간 블로그를 운영한 후, 블로그 플랫폼의 API를 사용해보기로 결정했습니다. 이리저리 사용해보다가 문득 깨닫습니다: 원격 블로그 서버와 통신하지 않고도 코드가 작동하는지 어떻게 알 수 있을까요?

public void deletePostsWithTag(Tag tag) {
  for (Post post : blogService.getAllPosts()) {
    if (post.getTags().contains(tag)) { blogService.deletePost(post.getId()); }
  }
}

가짜(Fakes)가 해결책입니다! 가짜는 실제 구현체처럼 작동하지만, 프로덕션 환경에 적합하지 않은 API의 가벼운 구현체입니다. 블로그 서비스의 경우, 중요한 것은 게시물을 가져오고 삭제하는 기능뿐입니다. 실제 블로그 서비스는 데이터베이스와 여러 프런트엔드 서버가 필요하겠지만, 코드를 테스트하는 데는 그런 것이 필요 없습니다. 필요한 것은 블로그 서비스 API의 어떤 구현체든 상관없습니다. 간단한 인메모리 구현체로 이를 달성할 수 있습니다.

public class FakeBlogService implements BlogService {    
  private final Set<Post> posts = new HashSet<>(); // Store posts in memory
  public void addPost(Post post) { posts.add(post); }
  public void deletePost(int id) {
    for (Post post : posts) {
      if (post.getId() == id) { posts.remove(post); return; }
    }
    throw new PostNotFoundException("No post with ID " + id);
  }
  public Set<Post> getAllPosts() { return posts; }
}

이제 테스트는 실제 블로그 서비스를 가짜로 바꿀 수 있으며, 테스트 대상 코드는 그 차이를 알지 못할 것입니다.

가짜는 실제 구현체를 테스트에서 사용할 수 없을 때 유용합니다. 예를 들어, 실제 구현체가 너무 느리거나(예: 시작하는 데 몇 분이 걸리는 경우) 비결정적인 경우(예: 테스트 실행 시 사용 불가능할 수 있는 외부 장치와 통신하는 경우)에 그렇습니다.

각 가짜는 실제 구현체를 소유한 사람이나 팀이 생성하고 유지 관리해야 하므로, 직접 가짜를 자주 작성할 필요는 없을 것입니다. 만약 가짜를 제공하지 않는 API를 사용하고 있다면, 직접 가짜를 만드는 것이 종종 쉽습니다. 테스트에서 사용할 수 없는 코드 부분을 감싸는 래퍼를 작성하고, 그 래퍼에 대한 가짜를 만드세요. 가짜는 가능한 가장 낮은 수준에서 생성해야 합니다(예: 테스트에서 데이터베이스를 사용할 수 없다면, 데이터베이스와 통신하는 모든 클래스를 가짜로 만드는 대신 데이터베이스를 가짜로 만드세요). 이렇게 하면 유지 관리할 가짜가 줄어들고, 테스트는 시스템의 중요한 부분에 대해 더 많은 실제 코드를 실행하게 됩니다.

가짜는 실제 구현체처럼 동작하는지 확인하기 위해 자체 테스트를 가져야 합니다(예: 실제 구현체가 특정 입력이 주어졌을 때 예외를 발생시키면, 가짜 구현체도 동일한 입력이 주어졌을 때 예외를 발생시켜야 합니다). 이를 수행하는 한 가지 방법은 API의 공개 인터페이스에 대해 테스트를 작성하고, 해당 테스트를 실제 구현체와 가짜 구현체 모두에 대해 실행하는 것입니다.

모든 테스트가 가짜를 사용하더라도 코드가 프로덕션에서 작동할 것이라고 여전히 완전히 신뢰하지 못한다면, 적은 수의 통합 테스트를 작성하여 코드가 실제 구현체와 함께 작동하는지 확인할 수 있습니다.