🔥딱 8일간! 인프런x토스x허먼밀러 역대급 혜택

[인프런 워밍업 스터디] Day 18 미션

인프런 ‘Readable Code: 읽기 좋은 코드를 작성하는 사고법’을 수강한 후, 작성한 내용입니다.

📌 @Mock, @MockBean, @Spy, @SpyBean, @InjectMocks

Mock과 Spy의 차이

  • Mock: 행위에 대한 기대를 명세하고, 그에 따라 동작하도록 만들어진 객체

  • Spy: Stub이면서 호출된 내용을 기록하여 보여줄 수 있는 객체

    • 일부는 실제 객체처럼 동작시키고 일부만 Stubbing할 수도 있다.

Stub이란?

테스트에서 요청한 것에 대해 미리 준비한 결과를 제공하는 객체. 그 외에는 응답하지 않는다.

여기서 그러면 ‘Mock이랑 Stub이랑 무슨 차이지? 같은 내용같은데?’라는 의문이 들 수 있다.

https://martinfowler.com/articles/mocksArentStubs.html

stub은 상태 검증, mock은 행위 검증과 관련되어 있다.

public interface MailService {
  public void send (Message msg);
}
public class MailServiceStub implements MailService {
  private List<Message> messages = new ArrayList<Message>();
  public void send (Message msg) {
    messages.add(msg);
  }
  public int numberSent() {
    return messages.size();
  }
}

// 테스트
  public void testOrderSendsMailIfUnfilled() {
    Order order = new Order(TALISKER, 51);
    MailServiceStub mailer = new MailServiceStub();
    order.setMailer(mailer);
    order.fill(warehouse);
    assertEquals(1, mailer.numberSent());
  }

Stub은 메일을 몇 번 보냈는지를 나타내는 상태를 검증한다.

// 테스트
  public void testOrderSendsMailIfUnfilled() {
    Order order = new Order(TALISKER, 51);
    Mock warehouse = mock(Warehouse.class);
    Mock mailer = mock(MailService.class);
    order.setMailer((MailService) mailer.proxy());

    mailer.expects(once()).method("send");
    warehouse.expects(once()).method("hasInventory")
      .withAnyArguments()
      .will(returnValue(false));

    order.fill((Warehouse) warehouse.proxy());
  }
}

Mock은 행위를 중심으로 검증한다.

@Mock, @MockBean

  • @Mock

    • 어노테이션이 붙은 객체를 Mock 객체로 생성

  • @MockBean

    • 어노테이션이 붙은 객체를 Mock 객체로 생성하고, Spring Context에 등록된 빈을 Mock 객체로 대체한다.

@Spy, SpyBean

  • @Spy

    • 어노테이션이 붙은 객체를 실제 객체 기반으로 만들지만, 일부 기능만 Stubbing 할 때 사용

  • @SpyBean

    • 해당 객체를 Spy 객체로 생성하고, Spring Context에 등록된 빈을 Spy 객체로 대체한다.

@InjectMocks

@InjectMocks 어노테이션이 붙은 객체가 필요로하는 필드 중 @Mock으로 생성된 객체를 주입해준다.

📌 BDD 스타일로 테스트 코드 배치하기

아래 3개의 테스트가 있다.

@BeforeEach 
void setUp() {
    ❓
} 

@DisplayName("사용자가 댓글을 작성할 수 있다.")
@Test
void writeComment() {
    1-1. 사용자 생성에 필요한 내용 준비
    1-2. 사용자 생성
    1-3. 게시물 생성에 필요한 내용 준비
    1-4. 게시물 생성
    1-5. 댓글 생성에 필요한 내용 준비
    1-6. 댓글 생성

    // given
    ❓

    // when
    ❓

    // then
    검증
}

@DisplayName("사용자가 댓글을 수정할 수 있다.")
@Test
void updateComment() {
    2-1. 사용자 생성에 필요한 내용 준비
    2-2. 사용자 생성
    2-3. 게시물 생성에 필요한 내용 준비
    2-4. 게시물 생성
    2-5. 댓글 생성에 필요한 내용 준비
    2-6. 댓글 생성
    2-7. 댓글 수정

    // given
    ❓

    // when
    ❓

    // then
    검증
}

@DisplayName("자신이 작성한 댓글이 아니면 수정할 수 없다.")
@Test
void cannotUpdateCommentWhenUserIsNotWriter() {
    3-1. 사용자1 생성에 필요한 내용 준비
    3-2. 사용자1 생성
    3-3. 사용자2 생성에 필요한 내용 준비
    3-4. 사용자2 생성
    3-5. 사용자1의 게시물 생성에 필요한 내용 준비
    3-6. 사용자1의 게시물 생성
    3-7. 사용자1의 댓글 생성에 필요한 내용 준비
    3-8. 사용자1의 댓글 생성
    3-9. 사용자2가 사용자1의 댓글 수정 시도

    // given
    ❓

    // when
    ❓

    // then
    검증        
}

내용을 살펴보고, 각 항목을 @BeforeEach, given절, when절에 배치한다면 어떻게 배치해야 할까?

(@BeforeEach에 올라간 내용은 공통 항목으로 합칠 수 있습니다. ex. 1-1과 2-1을 하나로 합쳐서 @BeforeEach에 배치)

게시판 게시물에 달리는 댓글을 담당하는 Service Test

댓글을 달기 위해서는 게시물과 사용자가 필요하다.

게시물을 올리기 위해서는 사용자가 필요하다.

직접 코드 배치해보기

@BeforeEach 
void setUp() {
    사용자 생성에 필요한 내용 준비
    사용자 생성
    
    사용자의 게시물 생성에 필요한 내용 준비
    사용자의 게시물 생성
} 

@DisplayName("사용자가 댓글을 작성할 수 있다.")
@Test
void writeComment() {
    // given
    댓글 생성에 필요한 내용 준비

    // when
    댓글 생성

    // then
    검증
}

@DisplayName("사용자가 댓글을 수정할 수 있다.")
@Test
void updateComment() {
    // given
    댓글 생성에 필요한 내용 준비
    댓글 생성

    // when
    댓글 수정

    // then
    검증
}
@DisplayName("자신이 작성한 댓글이 아니면 수정할 수 없다.")
@Test
void cannotUpdateCommentWhenUserIsNotWriter() {
    // given
    사용자2 생성에 필요한 내용 준비
    사용자2 생성
    
    사용자1의 댓글 생성에 필요한 내용 준비
    사용자1의 댓글 생성

    // when
    사용자2가 사용자 1의 댓글 수정 시도

    // then
    검증        
}
  • 공통된 준비 작업을 @BeforeEach로 분리

    • 댓글을 달기 위한 조건 작업

      • 게시물을 작성할 사용자 생성

      • 게시물 생성

  • given

    • 검증하고자 하는 행동에 필요한 작업 수행

      • 댓글 생성과 관련된 내용은 테스트마다 관리할 수 있도록 given 절에 배치

  • when

    • 검증하고자 하는 행동

댓글을 작성해보세요.

채널톡 아이콘