inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

Day 18 Mission [Layered Architecture 에 대하여 정리]

Troy
0

 1. @Mock, @MockBean, @Spy, @SpyBean, @InjectMocks 의 차이를 한번 정리해 봅시다.

 

1. @Mock vs @Spy (Mockito)

이 두 개는 Mockito에서 제공하는 어노테이션이고, 단위 테스트에서 가짜 객체(Mock 또는 Spy)를 생성할 때 사용된다.

@Mock

예제:

@Mock
private UserRepository userRepository;

@Test
void testMock() {
    when(userRepository.findById(1L)).thenReturn(Optional.of(new User(1L, "John")));

    Optional<User> user = userRepository.findById(1L);
    System.out.println(user.get().getName()); // John
}

@Spy

예제:

@Spy
private List<String> spyList = new ArrayList<>();

@Test
void testSpy() {
    spyList.add("one");
    spyList.add("two");

    when(spyList.size()).thenReturn(100); // 특정 메서드만 모킹

    System.out.println(spyList.get(0)); // one (실제 메서드 호출)
    System.out.println(spyList.size()); // 100 (모킹된 값 반환)
}

2. @MockBean vs @SpyBean (Spring Boot)

이 두 개는 Spring Boot에서 제공하는 어노테이션이다.
Spring 컨테이너에 있는 빈(Bean)을 Mock 또는 Spy로 대체하는 역할을 한다.

@MockBean

예제:

@SpringBootTest
class MyServiceTest {
    
    @MockBean
    private UserRepository userRepository;

    @Autowired
    private MyService myService;

    @Test
    void testMockBean() {
        when(userRepository.findById(1L)).thenReturn(Optional.of(new User(1L, "John")));

        User user = myService.getUserById(1L);
        System.out.println(user.getName()); // John
    }
}

MockBean을 사용하면 UserRepository의 실제 빈이 Mock으로 대체됨.


@SpyBean

예제:

@SpringBootTest
class MyServiceTest {

    @SpyBean
    private UserService userService; // 기존 Bean을 Spy로 감싸서 일부 메서드만 Mock 가능

    @Test
    void testSpyBean() {
        when(userService.getUserName(1L)).thenReturn("Mocked Name");

        System.out.println(userService.getUserName(1L)); // Mocked Name (모킹된 메서드)
        System.out.println(userService.getUserCount());  // 실제 메서드 실행
    }
}

SpyBean을 사용하면 UserService의 실제 빈이 Spy로 감싸짐.


3. @InjectMocks

예제:

class UserService {
    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public String getUserName(Long id) {
        return userRepository.findById(id).map(User::getName).orElse("Unknown");
    }
}

class UserServiceTest {

    @Mock
    private UserRepository userRepository;

    @InjectMocks
    private UserService userService; // userRepository가 자동 주입됨

    @Test
    void testInjectMocks() {
        when(userRepository.findById(1L)).thenReturn(Optional.of(new User(1L, "Alice")));

        System.out.println(userService.getUserName(1L)); // Alice
    }
}

userService를 직접 new로 생성하지 않아도 @InjectMocks@MockuserRepository를 자동으로 주입해 준다.

 

2. 아래 3개의 테스트가 있습니다.
내용을 살펴보고, 각 항목을 @BeforeEach, given절, when절에 배치한다면 어떻게 배치하고 싶으신가요?
(@BeforeEach에 올라간 내용은 공통 항목으로 합칠 수 있습니다. ex. 1-1과 2-1을 하나로 합쳐서 @BeforeEach에 배치)

@BeforeEach 
void setUp() {
    1-1. 2-1. 3-1. 사용자 생성에 필요한 내용 준비
    1-3. 2-3. 3-5. 게시물 생성에 필요한 내용 준비
    1-2. 2-2. 3-2. 사용자 생성
    1-4. 2-4. 3-6. 게시물 생성
}

@DisplayName("사용자가 댓글을 작성할 수 있다.")
@Test
void writeComment() {

    // given
    1-5. 댓글 생성에 필요한 내용 준비

    // when
    1-6. 댓글 생성

    // then
    검증
}

@DisplayName("사용자가 댓글을 수정할 수 있다.")
@Test
void updateComment() {

    // given
    2-5. 댓글 생성에 필요한 내용 준비
    2-6. 댓글 생성

    // when
    2-7. 댓글 수정

    // then
    검증
}

@DisplayName("자신이 작성한 댓글이 아니면 수정할 수 없다.")
@Test
void cannotUpdateCommentWhenUserIsNotWriter() {

    // given
    3-3. 사용자2 생성에 필요한 내용 준비
    3-4. 사용자2 생성
    3-7. 사용자1의 댓글 생성에 필요한 내용 준비
    3-8. 사용자1의 댓글 생성

    // when
    3-9. 사용자2가 사용자1의 댓글 수정 시도

    // then
    검증        
}

답변 0