요소 | 모키시스트(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 해주면 편하게 작업하였지만, 클래시스트는 의존성에 대한 클래스를 일일이 다 구현해줘야해서 생각보다 오래걸렸다.