


| 요소 | 모키시스트(Mockist) | 클래시스트(Classicist) |
|---|---|---|
| 테스트 중점 | 행동 | 상태 |
| 테스트 포커스 | 상호 작용 | 결과 |
| 모킹 사용 | 네 | 아니요 |
| 테스트 복잡성 | 낮음 | 높음 |
| 외부 의존성 | 낮음 | 높음 |
public class UserRegisterController {
private final RegisterUserUseCase registerUserUseCase;
private final UserResponseMapper userResponseMapper;
@PostMapping("/register")
public ResponseEntity<T> registerUser(
@RequestBody RegisterUserRequest registerUserRequest
) {
RegisterUserCommand command = RegisterUserCommand.builder()
.username(registerUserRequest.getUsername())
// 등등
.build();
User user = registerUserUseCase.registerUser(command);
RegisterUserResponse response = userResponseMapper.mapToRegisterUserResponse(user);
return ResponseEntity.status(HttpStatus.OK).body(response);
}
}@ExtendWith(MockitoExtension.class)
@DisplayName("UserRegisterController 단위 테스트")
public class UserRegisterControllerTest {
@Mock
private RegisterUserUseCase registerUserUseCase;
@Mock
private UserResponseMapper userResponseMapper;
@InjectMocks
private UserRegisterController userRegisterController;
RegisterUserRequest request;
User mockUser;
RegisterUserResponse mockResponse;
@BeforeEach
public void setUp() {
// RegisterUserRequest 객체 생성
request = new RegisterUserRequest(
"testuser",
// 등등(필요에 따라 추가)
);
// User 객체 생성
mockUser = User.builder()
.userId(1L)
// 등등(필요에 따라 추가)
.build();
// RegisterUserResponse 객체 생성
mockResponse = RegisterUserResponse.builder()
.username("testuser")
// 등등(필요에 따라 추가)
.build();
}
// 밑에서 이어짐
}UserRegisterControllerTest코드가 너무 길어 두 분류로 나눠서 설명을 하겠다.@ExtendWith(MockitoExtension.class) → 해당 범위에서 Mockito의 기능을 사용할 수 있게 해준다.RegisterUserUseCase, UserResponseMapper → 이 두 부분은 UserRegisterController에서 의존성으로 @Mock을 등록하여 실제 객체를 만드는 것이 아닌 가짜 객체를 만들어 등록을 해주기 위함이다.UserRegisterController → @InjectMocks을 위에서 등록한 두 Mock 객체를 사용할 수 있도록 설정해둔다.RegisterUserUseCase, UserResponseMapper)를 직접 구현하지 않아도 되고 UserRegisterController에 로직만 명확하게 테스트가 가능하다.@BeforeEach → 모든 테스트가 실행 되기 전에 먼저 실행 되는 메소드이다.@ExtendWith(MockitoExtension.class)
@DisplayName("UserRegisterController 단위 테스트")
public class UserRegisterControllerTest {
@Test
@DisplayName("회원가입 요청에 대한 성공 응답 반환")
public void shouldRegisterUser() throws Exception {
// given
when(registerUserUseCase.registerUser(any())).thenReturn(mockUser);
when(userResponseMapper.mapToRegisterUserResponse(any())).thenReturn(mockResponse);
// when
ResponseEntity<ReturnObject> response = userRegisterController.registerUser(request);
// then
assertEquals(HttpStatus.OK, response.getStatusCode());
assertEquals(mockResponse, response.getBody().getData());
verify(registerUserUseCase, times(1)).registerUser(any());
verify(userResponseMapper, times(1)).mapToRegisterUserResponse(any());
}
}shouldRegisterUser)에 대한 테스트 코드를 보겠다.when(registerUserUseCase.registerUser(any())).thenReturn(mockUser) → 의존성으로 주입 받은 registerUserUseCase클래스에 registerUser 메소드에 아무 값(any())이 들어가도 그 결과 값은 꼭 mockUser나오게 미리 가정을 해두는 것이다.when(userResponseMapper.mapToRegisterUserResponse(any())).thenReturn(mockResponse) → 의존성으로 주입 받은 userResponseMapper클래스에 mapToRegisterUserResponse메소드에 아무 값(any())이 들어가도 그 결과 값은 꼭 mockResponse나오게 미리 가정을 해두는 것이다.userRegisterController.registerUser(request) → 우린 위에서 @BeforeEach를 통해 request를 미리 만들어두어 바로 사용이 가능하다.assertEquals(HttpStatus.OK, response.getStatusCode()) → 응답 상태 코드가 OK(200)인지 확인한다.assertEquals(mockResponse, response.getBody().getData()) → 응답 본문의 데이터가 우리가 mocking한 response 객체와 동일한지 확인한다.verify(registerUserUseCase, times(1)).registerUser(any()) → UserRegisterController에서 registerUserUseCase.registerUser()가 1번 실행됐는지 확인하기.verify(userResponseMapper, times(1)).mapToRegisterUserResponse(any()) → UserRegisterController에서 userResponseMapper.mapToRegisterUserResponse()가 1번 실행됐는지 확인하기.@DisplayName("UserRegisterController 단위 테스트")
public class UserRegisterControllerTest {
private UserRegisterController userRegisterController;
private RegisterUserUseCase registerUserUseCaseFake;
private UserResponseMapper userResponseMapperFake;
@BeforeEach
void setUp() {
registerUserUseCaseFake = new RegisterUserUseCaseFake();
userResponseMapperFake = new UserResponseMapperFake();
userRegisterController = new UserRegisterController(registerUserUseCaseFake, userResponseMapperFake);
}
private static class RegisterUserUseCaseFake implements RegisterUserUseCase {
@Override
public User registerUser(RegisterUserCommand command) {
return User.builder()
.username(command.getUsername())
.build();
}
}
private static class UserResponseMapperFake extends UserResponseMapper {
@Override
public RegisterUserResponse mapToRegisterUserResponse(User user) {
return RegisterUserResponse.builder()
.username(user.getUsername())
.build();
}
// UserRegisterController에서는 사용하지 않는 메소드라 구현 X
@Override
public LoginUserResponse mapToLoginUserResponse(User user) {
return null;
}
}
}UserRegisterControllerTest코드가 너무 길어 두 분류로 나눠서 설명을 하겠다.RegisterUserUseCase, UserResponseMapper)에 대한 구현체(RegisterUserUseCaseFake, UserResponseMapperFake)를 만들어주어야한다.@BeforeEach → UserRegisterController에 대한 의존성(Fake)을 생성자로 초기화 시켜준다.class RegisterUserUseCaseFake implements RegisterUserUseCase → RegisterUserUseCase 인터페이스의 registerUser 메소드를 오버라이딩한다.class UserResponseMapperFake extends UserResponseMapper → UserResponseMapper 인터페이스의 mapToRegisterUserResponse, mapToLoginUserResponse메소드를 오버라이딩하고 구현한다.UserRegisterController클래스에서 mapToLoginUserResponse를 사용하지 않기에 디테일하게 구현하지 않았다.@DisplayName("UserRegisterController 단위 테스트")
public class UserRegisterControllerTest {
@Test
@DisplayName("회원가입 요청에 대한 성공 응답 반환")
void testRegisterUserSuccess() {
// given
RegisterUserRequest request = new RegisterUserRequest(
"testuser123",
"Test@12345",
"Test@12345",
"testNickname",
"010-1234-5678",
"test@example.com"
);
// when
ResponseEntity<ReturnObject> responseEntity = userRegisterController.registerUser(request);
// then
assertEquals(HttpStatus.OK, responseEntity.getStatusCode());
assertNotNull(responseEntity.getBody());
assertEquals(request.getUsername(), ((RegisterUserResponse) responseEntity.getBody().getData()).getUsername());
}
}testRegisterUserSuccess) 메소드에 대한 테스트 코드를 보겠다.UserRegisterController에서 registerUser메소드에 들어가는 request를 정의해준다.assertEquals(HttpStatus.OK, responseEntity.getStatusCode()) → 실제 값에서 200ok가 나왔는지 확인한다.assertNotNull(responseEntity.getBody()) → 실제 값이 있는지 판단한다.assertEquals(request.getUsername(), ((RegisterUserResponse) responseEntity.getBody().getData()).getUsername()) → request로 받은 유저 네임과 실제 반환된 값에 유저 네임이 같은지 확인한다.UserRegisterController)를 테스트를 하려니 모키시스트는 의존성에 대한 클래스를 Mocking 해주면 편하게 작업하였지만, 클래시스트는 의존성에 대한 클래스를 일일이 다 구현해줘야해서 생각보다 오래걸렸다.