- 가장 기본이 되는 회원가입 API를 가져왔다. 해당 코드를 모키시스트로 작성할 예정이다.
- UserRegisterController.java
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);
}
}
- application-test.yml
spring:
datasource:
url: jdbc:h2:mem:testdb;MODE=MySQL
driver-class-name: org.h2.Driver
username: sa
password: password
h2:
console:
enabled: true
jpa:
hibernate:
ddl-auto: create-drop
dialect: org.hibernate.dialect.MySQL5InnoDBDialect
show-sql: true
database-platform: org.hibernate.dialect.H2Dialect
url: jdbc:h2:mem:testdb;MODE=MySQL
→ H2 데이터베이스가 MySQL(MariaDB)의 문법을 따르게 된다.
- 특정 쿼리가 MySQL의 기본모드에서는 작동하지만 H2에서는 작동을 안 할수도 있다.
- 이 문제는 맨 밑에 결론(몰랐던 점)부분에서 다루겠다.
dialect: org.hibernate.dialect.MySQL5InnoDBDialect
→ MySQL 5 버전의 InnoDB 스토리지 엔진에 특화된 SQL 생성 및 데이터베이스 작업을 지원한다.
- 나머지는 우리가 익히 알고 있는 설정들이라 넘어가겠다.
- UserRegisterControllerTest.java (1)
@Transactional
@AutoConfigureMockMvc
@ActiveProfiles("test")
@SpringBootTest(classes = UserServiceApplication.class)
@DisplayName("UserRegisterControllerTest 통합 테스트")
public class UserRegisterControllerTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
}
@Transactional
→ 테스트 메서드가 트랜잭션 내에서 실행되도록 지정한다.
- 테스트가 종료되면 트랜잭션이 롤백되어, 테스트에서 수행한 데이터베이스 변경이 실제 데이터베이스에 영향을 미치지 않게 됩니다.
- 이는 테스트 간의 독립성을 보장한다.
- 데이터베이스 상태를 초기화하여 다음 테스트에 영향을 미치지 않도록 한다.
@AutoConfigureMockMvc
→ MockMvc 인스턴스를 자동으로 구성합니다.
- MockMvc는 Spring MVC의 테스트를 도와주는 클래스로, HTTP 요청을 DispatcherServlet에 전송하고 결과를 받아 검증할 수 있게 해줍니다.
@ActiveProfiles("test")
→ 위에서 만들어준 application-test.yml를 사용하기 위함.
@SpringBootTest(classes = UserServiceApplication.class)
→ classes 속성에 지정된 UserServiceApplication클래스를 사용하여 애플리케이션 컨텍스트를 로드합니다.
- MockMvc → MockMvc를 사용하여 HTTP 요청을 시뮬레이션하고 응답을 검증할 수 있습니다.
- ObjectMapper → ObjectMapper는 JSON과 자바 객체간의 변환을 담당하는 클래스입니다. 테스트에서는 주로 HTTP 요청 본문을 JSON 문자열로 변환하거나, HTTP 응답 본문의 JSON 문자열을 자바 객체로 변환하는데 사용됩니다.
- UserRegisterControllerTest.java (2)
public class UserRegisterControllerTest {
@Test
@DisplayName("회원가입 요청에 대한 성공 응답 반환")
public void registerUserTest() throws Exception {
RegisterUserRequest registerUserRequest = new RegisterUserRequest(
"testuser01",
"Test@1234",
"Test@1234",
"testnickname",
"010-1234-5678",
"testuser01@example.com"
);
mockMvc.perform(post("/users/register")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(registerUserRequest)))
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.success").value(true));
}
}
- 해당 테스트는 흐름 테스트이다.
RegisterUserRequest
→ 실제로 매개변수로 들어가는 객체를 생성해준다.
mockMvc.perform()
→ MockMvc 인스턴스를 사용하여 HTTP 요청을 시뮬레이션한다.
post("/users/register")
→ HTTP POST 메서드를 사용하여 "/users/register" 경로에 요청을 보낸다.
contentType(MediaType.APPLICATION_JSON)
→ 요청 본문의 Content-Type 헤더를 "application/json"으로 설정한다.
content(objectMapper.writeValueAsString(registerUserRequest))
→ registerUserRequest 객체를 JSON 문자열로 변환하여 요청 본문으로 설정한다.
- UserRegisterControllerTest.java (3)
public class UserRegisterControllerTest {
@Autowired
private UserJpaRepo userJpaRepo;
@BeforeEach
public void setup() {
UserJpaEntity duplicateUsernameEntity = new UserJpaEntity();
duplicateUsernameEntity.setUsername("username");
duplicateUsernameEntity.setPassword("Test@1234");
duplicateUsernameEntity.setNickname("uniqueNickname");
duplicateUsernameEntity.setPhone("010-1234-5670");
duplicateUsernameEntity.setEmail("uniqueEmail@example.com");
duplicateUsernameEntity.setRole(Role.USER);
userJpaRepo.save(duplicateUsernameEntity);
}
@Test
@DisplayName("회원가입 요청에 사용자 이름이 중복될 경우 실패 응답 반환")
public void registerUserTest_UsernameDuplicate() throws Exception {
RegisterUserRequest registerUserRequest = new RegisterUserRequest(
"username",
"Test@1234",
"Test@1234",
"testnickname",
"010-1234-5678",
"testuser01@example.com"
);
mockMvc.perform(post("/users/register")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(registerUserRequest)))
.andDo(print())
.andExpect(status().isConflict());
}
}
- 해당 테스트는 예외 처리 테스트이다.
UserJpaRepo
→ UserJpaRepo를 사용하기 위해 빈 등록을 해주었다.
@BeforeEach
→ 각 테스트 메서드 실행 전에 실행되는 메소드로 duplicateUsernameEntity를 userJpaRepo에 저장하였다. (username 중복 테스트를 위해 미리 저장해둠.)
mockMvc.perform(post("/users/register")
→ 아까와 동일하게 진행을 하였다.
andExpect(status().isConflict())
→ 기대한 값은 409(conflict)이다.
- UserRegisterControllerTest.java (4)
public class UserRegisterControllerTest {
@Test
@DisplayName("회원가입 요청에 비밀번호 길이가 너무 짧을 경우 실패 응답 반환")
public void registerUserTest_ShortPassword() throws Exception {
RegisterUserRequest registerUserRequest = new RegisterUserRequest(
"testuser03",
"short",
"short",
"testnickname",
"010-1234-5678",
"testuser03@example.com"
);
mockMvc.perform(post("/users/register")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(registerUserRequest)))
.andDo(print())
.andExpect(status().isBadRequest());
}
}
- 해당 테스트는 경계 값 분석 테스트이다.
- 경계 값 분석 테스트란
- UserRegisterControllerTest.java (5)
public class UserRegisterControllerTest {
@Test
@DisplayName("회원가입 API 성능 테스트")
public void registerUserTest_Performance() throws Exception {
long startTime = System.currentTimeMillis();
int testCount = 100;
for(int i = 0; i < testCount; i++) {
RegisterUserRequest registerUserRequest = new RegisterUserRequest(
"testuser" + i,
"Test@1234",
"Test@1234",
"testnickname" + i,
"010-1234-5678",
"testuser" + i + "@example.com"
);
mockMvc.perform(post("/users/register")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(registerUserRequest)))
.andExpect(status().isOk());
}
long endTime = System.currentTimeMillis();
System.out.println("Total Time for " + testCount + " requests: " + (endTime - startTime) + "ms");
}
}
- 해당 테스트는 성능 테스트이다.
- 100회의 회원가입 요청을 보내고 전체 수행 시간을 측정한다.
- 지금은 Low Level에 테스트를 진행하는거라 간단하게 작성했지만 JMeter 같은 도구를 이용하면 정교한 성능 테스트가 가능하다.