Contents
🧠 결론 요약🌐 1. 통합 테스트 환경 구성 어노테이션
@AutoConfigureMockMvc
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
🔹 @SpringBootTest(webEnvironment = ...)
전체 Spring 컨텍스트를 로드하고, 테스트할 웹 환경을 선택하는 설정.
옵션 | 설명 |
MOCK | 내장 톰캣 없이 Mock 환경에서 테스트 (실제 HTTP 포트 사용 X) ✅ 가장 많이 씀 |
RANDOM_PORT | 내장 톰캣을 사용하고 랜덤 포트에서 서버 실행 |
DEFINED_PORT | application.yml에서 지정한 포트로 실행 |
NONE | 웹 환경 로딩 X, 순수한 자바 테스트처럼 사용 |
🔹 @AutoConfigureMockMvc
MockMvc
객체를 자동으로 등록 (IoC 컨테이너에 Bean으로 등록됨)
- 직접
new MockMvc()
할 필요 없음
- 단위 테스트와 달리
@WebMvcTest
와 함께 쓰지 않음 →SpringBootTest
로 전체 컨텍스트를 로드하기 때문
🔧 2. 테스트 코드 구성 요소
@Autowired
private ObjectMapper om;
@Autowired
private MockMvc mvc;
🔹 ObjectMapper
- Jackson 라이브러리 제공 객체
- 자바 객체 ↔ JSON 문자열 변환 도구
- ✅ 예:
om.writeValueAsString(obj)
→String
타입 JSON 반환
UserRequest.JoinDTO dto = new UserRequest.JoinDTO();
dto.setUsername("haha");
...
String requestBody = om.writeValueAsString(dto);
💡 JavaScript에서는 JSON.stringify() 와 비슷한 역할💡 실제 API 환경에서는RestTemplate
,WebClient
사용함 (Spring Boot)
🔹 MockMvc
- 웹 계층을 서버 띄우지 않고 테스트하는 객체
- 실제처럼 요청을 만들고, 응답을 받아 assert 가능
🚀 3. 테스트 요청 구성
ResultActions actions = mvc.perform(
MockMvcRequestBuilders.post("/join")
.content(requestBody)
.contentType(MediaType.APPLICATION_JSON)
);
✅ 요청 구성 요약
POST /join
: 요청 경로
.content(requestBody)
: 바디에 JSON 데이터 전송
.contentType(MediaType.APPLICATION_JSON)
: 헤더 설정 (Content-Type)
🖨️ 4. 응답 출력 (eye)
String responseBody = actions.andReturn().getResponse().getContentAsString();
System.out.println(responseBody);
- 응답 객체(MockHttpServletResponse)의 내용을
String
으로 읽음
- 터미널에 찍어보며 실제 결과 확인
✅ 예시 응답:
{
"status": 200,
"msg": "성공",
"body": {
"id": 4,
"username": "haha",
"email": "haha@nate.com",
"createdAt": "2025-05-13 12:40:50.879215"
}
}
✅ 5. then: 결과 검증
actions.andExpect(jsonPath("$.status").value(200));
actions.andExpect(jsonPath("$.msg").value("성공"));
actions.andExpect(jsonPath("$.body.id").value(4));
actions.andExpect(jsonPath("$.body.username").value("haha"));
actions.andExpect(jsonPath("$.body.email").value("haha@nate.com"));
jsonPath()
는 JSON 응답에서 특정 경로의 값을 가져와 검증
value()
로 기대값과 비교
❗ 6. 결과 값이 틀릴 때 로그 예시
예:
body.id
가 예상 3
인데 실제 4
일 경우MockHttpServletRequest:
HTTP Method = POST
Request URI = /join
Parameters = {}
Headers = [Content-Type:"application/json;charset=UTF-8", Content-Length:"61"]
Body = {"username":"haha","password":"1234","email":"haha@nate.com"}
Session Attrs = {}
Handler:
Type = shop.mtcoding.blog.user.UserController
Method = shop.mtcoding.blog.user.UserController#join(JoinDTO, Errors)
Resolved Exception:
Type = null
MockHttpServletResponse:
Status = 200
Content type = application/json;charset=UTF-8
Body = {"status":200,"msg":"성공","body":{"id":4,"username":"haha","email":"haha@nate.com","createdAt":"2025-05-13 12:41:38.091431"}}
JSON path "$.body.id"
Expected :3
Actual :4
🧠 로그 파악 포인트
항목 | 설명 |
MockHttpServletRequest | 어떤 요청을 보냈는지 확인 |
Handler | 어느 컨트롤러가 처리했는지 |
MockHttpServletResponse | 어떤 응답을 보냈는지 |
JSON path | 어떤 값이 기대와 달랐는지 ( Expected vs Actual ) |
📌 결론: 통합 테스트란?
목적 | 실제 요청/응답 환경과 최대한 유사하게 API를 테스트 |
이점 | 1) 서버를 띄우지 않음2) 빠름3) DB까지 연동되므로 실전과 유사 |
사용 도구 | @SpringBootTest , @AutoConfigureMockMvc , MockMvc , ObjectMapper 등 |
주의 | 테스트 데이터를 조작하므로 테스트 후 롤백하거나 분리된 DB 사용 권장 |
🧩 번외: 만약 H2가 아니라 MySQL로 테스트했다면 벌어질 수 있는 현실적인 테스트 문제
테스트 코드를 짜다 보면 조회 테스트를 하고 싶은데, 더미 데이터가 없어서 막히는 경우가 많다.
→ 그래서 insert 먼저 하고, 그 데이터를 바탕으로 조회 테스트하고 싶어짐
✅ 하지만 이건 테스트 원칙을 어기는 것
❌ 잘못된 흐름
- 테스트1: insert
- 테스트2: select
→ 테스트 1의 결과에 의존
하지만 테스트는 기본적으로 독립적이어야 함.
(순서에 의존 X, 이전 테스트 결과 X)
🧨 왜 어려운가? 테스트 롤백 + DB 상태 유지 문제
- 대부분 테스트에는
@Transactional
이 붙어서 테스트 종료 시 자동 롤백
- 그런데 다음 테스트에서 select 하려면 insert가 필요 → 롤백되면 select가 실패
- 반대로 롤백 안 하고
@Rollback(false)
설정하면
→ DB가 오염되거나, 중복 데이터로 인해 테스트가 다시 실패하는 일이 발생
🙅♂️ 이게 바로 실무에서 테스트 어렵다는 이유
- 특히
H2
안 쓰고MySQL
쓰는 경우, 로컬 DB 상태에 따라 테스트가 불안정
- 그래서 회사에선 더미 데이터 관리 전략이 반드시 필요함
💡 실무 해결 전략
방법 | 설명 |
@Sql 어노테이션 | 테스트 전 미리 insert SQL 실행 |
@BeforeEach | 자바 코드로 매 테스트마다 더미 객체 생성 |
@Rollback(false) | 특정 테스트에선 롤백하지 않음 (주의 필요) |
테스트 전용 DB | 테스트 전용 MySQL DB를 따로 분리해서 운영 |
테스트 전용 서비스 계층 | insert 후 select 테스트 흐름 분리 가능 |
🔄 예시: 테스트 중 더미 데이터 수동 삽입
@BeforeEach
public void setUp() {
User user = new User("testuser", "1234", "test@aaa.com");
userRepository.save(user);
}
✅ 이렇게 하면 모든 테스트에서 testuser는 존재하므로 조회/중복 검증이 가능
🧠 결론 요약
- 중복 체크는 insert 지점에서 한 번 더 검증하는 게 설계적으로 맞다
- 테스트는 독립적으로! 서로 의존하거나 순서에 따라 다르면 유지보수 불가능
- 테스트가 어려운 이유는 DB 상태가 계속 바뀌기 때문 →
@Sql
,@BeforeEach
, 전용 DB 활용이 해답
- 실무에서는
MockMvc
,ObjectMapper
,@SpringBootTest
를 활용한 통합 테스트가 주력이며, 이 과정에서 데이터 세팅이 가장 중요함
Share article