단위 테스트(Unit Test)란?
단위 테스트는 소프트웨어 개발 과정에서 가장 작은 단위의 코드, 예를 들어 함수나 메서드 등을 대상으로 하는 테스트입니다. 단위 테스트의 목적은 각 단위가 개별적으로 정상적으로 동작하는지 확인하는 것입니다.
단위 테스트는 일반적으로 개발자에 의해 작성되며, 테스트 프레임워크를 사용하여 자동화됩니다. 예를 들어, Java에서는 JUnit, Python에서는 PyTest와 같은 테스트 프레임워크를 사용할 수 있습니다.
단위 테스트는 빠르게 실행되며, 개발 초기 단계에서 버그를 발견하고 수정하는 데 매우 효과적입니다. 왜냐하면 단위 테스트는 소프트웨어의 작은 부분만을 대상으로 하기 때문에, 버그의 원인을 빠르게 파악할 수 있습니다.
또한, 단위 테스트는 코드의 변경이 다른 부분에 미치는 영향을 확인하는 데에도 유용합니다. 이를 통해 개발자는 코드를 리팩토링하거나 기능을 추가할 때 더욱 자신감을 가질 수 있습니다.
단위 테스트의 예로는, 특정 입력에 대해 함수가 예상대로 출력을 반환하는지 검증하는 테스트가 있습니다. 이러한 테스트를 통해 개발자는 함수가 정확하게 동작함을 보장할 수 있습니다.
통합 테스트(Integration Test)란?
통합 테스트는 두 개 이상의 단위가 함께 잘 작동하는지 확인하는 테스트입니다. 이는 단위 테스트에서 검증된 개별 단위들이 시스템 전체에서도 올바르게 동작하는지 확인하기 위해 수행됩니다.
통합 테스트는 소프트웨어의 모듈이나 서비스가 서로 올바르게 통신하는지, 데이터가 정확하게 전달되는지 등을 검증합니다. 이를 통해 개발자는 시스템의 다양한 부분이 함께 잘 작동함을 확인할 수 있습니다.
통합 테스트는 일반적으로 단위 테스트보다 실행 시간이 더 길며, 복잡합니다. 왜냐하면 통합 테스트는 소프트웨어의 여러 부분을 함께 테스트하기 때문입니다. 따라서 통합 테스트는 보통 개발의 후반 단계에서 수행됩니다.
통합 테스트의 예로는, 웹 애플리케이션에서 사용자가 로그인을 시도할 때, 인증 시스템과 데이터베이스가 올바르게 통신하는지 검증하는 테스트가 있습니다. 이러한 테스트를 통해 개발자는 시스템의 전체적인 동작을 검증할 수 있습니다.
왜냐하면 통합 테스트는 시스템의 다양한 부분이 서로 올바르게 통신하고, 데이터가 정확하게 전달되는지 확인하기 때문입니다. 이는 소프트웨어의 전체적인 품질을 보장하는 데 중요한 역할을 합니다.
@AutoConfigureMockMvc
public class QnaControllerTest extends IntegrationTestSupport {
@Autowired
private QnaService qnaService;
@Autowired
private QnaRepository qnaRepository;
@Autowired
private StoreRepository storeRepository;
@Autowired
private UserRepository userRepository;
@Autowired
private JwtUtil jwtUtil;
@Autowired
private RegionRepository regionRepository;
@DisplayName("Qna를 수정한다")
@Test
void updateAiQna() throws Exception {
User owner = userRepository.save(
new User("new_user", "password123", UserRoleEnum.MASTER)
);
String token = jwtUtil.createToken("new_user", UserRoleEnum.MASTER);
Region region = regionRepository.save(new Region("서울"));
Store store = storeRepository.save(
Store.builder()
.address("경기도 성남시 분당구 가로 1")
.content("굽네치킨입니다.")
.name("굽네치킨")
.region(region) // UUID 대신 객체를 직접 전달
.owner(owner) // UUID 대신 객체를 직접 전달
.build()
);
Store testStore = storeRepository.save(store);
Qna qna = qnaRepository.save(new Qna("Old Question", "Old Answer", testStore));
UUID qnaId = qna.getAiId();
QnaRequestDto requestDto = new QnaRequestDto(qna.getAiId(), "Updated Question",
"Updated Answer", store.getId().toString());
// When
QnaResponseDto responseDto = qnaService.updateQna(requestDto, qnaId);
// Then
assertThat(responseDto)
.isNotNull()
.extracting(QnaResponseDto::getQuestion, QnaResponseDto::getAnswer)
.containsExactly("Updated Question", "Updated Answer");
// content 필드 확인
}
}
해당 테스트 클래스는 **통합 테스트(Integration Test)**입니다.
🔹 이유: 왜 통합 테스트인가?
- @SpringBootTest를 사용하지 않았지만, IntegrationTestSupport를 상속받고 있음
- IntegrationTestSupport가 @SpringBootTest를 포함하고 있을 가능성이 큽니다.
- 즉, Spring Context(빈, DB 연결 등)를 로드하는 통합 테스트일 가능성이 높음.
- @AutoConfigureMockMvc가 사용됨
- @AutoConfigureMockMvc는 컨트롤러를 포함한 Spring MVC의 전체 흐름을 테스트하기 위해 사용.
- 일반적으로 MockMvc를 사용한 컨트롤러 테스트는 통합 테스트의 일부로 볼 수 있음.
- 실제 Repository 및 Service 빈을 @Autowired로 주입받아 사용
- qnaRepository, storeRepository, userRepository 등의 JPA Repository를 주입받아 실제 DB와 연동된 테스트를 수행.
- 이는 단위 테스트가 아니라 실제 데이터베이스가 필요한 통합 테스트에 해당.
- 테스트 과정에서 실제 데이터를 저장하고, 수정하고 있음
- userRepository.save(), storeRepository.save(), qnaRepository.save() 등을 통해 DB에 실제 데이터를 삽입 후 수정하는 테스트를 수행.
- 즉, 특정 메서드만 테스트하는 것이 아니라 애플리케이션의 여러 계층(Repository, Service, Controller)이 함께 동작하는지를 검증.
🔹 만약 단위 테스트(Unit Test)로 바꾸려면?
단위 테스트로 변경하려면, Spring Context를 로드하지 않고, 필요한 객체를 Mock으로 처리해야 합니다.
✅ 1. @SpringBootTest 없이 @WebMvcTest를 사용 (Controller 단위 테스트)
@WebMvcTest(QnaController.class)
@AutoConfigureMockMvc
public class QnaControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private QnaService qnaService; // Service를 Mock으로 대체
@Test
void updateAiQna() throws Exception {
UUID qnaId = UUID.randomUUID();
QnaResponseDto responseDto = new QnaResponseDto(qnaId, "Updated Question", "Updated Answer");
// QnaService의 동작을 Mocking
when(qnaService.updateQna(any(QnaRequestDto.class), any(UUID.class)))
.thenReturn(responseDto);
QnaRequestDto requestDto = new QnaRequestDto(qnaId, "Updated Question",
"Updated Answer", "1");
mockMvc.perform(put("/api/qna/" + qnaId)
.contentType(MediaType.APPLICATION_JSON)
.content(new ObjectMapper().writeValueAsString(requestDto)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.question").value("Updated Question"))
.andExpect(jsonPath("$.answer").value("Updated Answer"));
}
}
✅ @WebMvcTest를 사용하여 컨트롤러만 테스트
✅ @MockBean으로 QnaService를 Mocking하여 실제 데이터베이스 접근 없이 테스트 가능
✅ 2. Service 단위 테스트로 변경
Service 계층만 단위 테스트하려면 @SpringBootTest를 사용하지 않고, @MockBean을 활용해야 합니다.
@ExtendWith(MockitoExtension.class)
class QnaServiceTest {
@InjectMocks
private QnaService qnaService;
@Mock
private QnaRepository qnaRepository;
@Test
void updateAiQna() {
UUID qnaId = UUID.randomUUID();
Qna qna = new Qna("Old Question", "Old Answer", null);
when(qnaRepository.findById(qnaId)).thenReturn(Optional.of(qna));
QnaRequestDto requestDto = new QnaRequestDto(qnaId, "Updated Question",
"Updated Answer", "1");
QnaResponseDto responseDto = qnaService.updateQna(requestDto, qnaId);
assertThat(responseDto)
.isNotNull()
.extracting(QnaResponseDto::getQuestion, QnaResponseDto::getAnswer)
.containsExactly("Updated Question", "Updated Answer");
}
}
✅ @Mock을 사용하여 QnaRepository를 Mocking
✅ 실제 DB를 사용하지 않고, 특정 동작만을 검증하여 빠른 테스트 가능
🔹 결론
✅ 현재 코드는 "통합 테스트(Integration Test)"
- Spring Context를 로드하고, 실제 데이터베이스를 사용하며, 여러 계층(Service, Repository)이 함께 동작하는지 검증하기 때문.
✅ 단위 테스트(Unit Test)로 변경하려면?
- Controller 단위 테스트 → @WebMvcTest + @MockBean을 사용하여 QnaService Mocking
- Service 단위 테스트 → @Mock을 활용하여 QnaRepository를 Mocking
'TIL' 카테고리의 다른 글
StackOverflowError:null (feat.AuthenticationManager) (0) | 2025.03.07 |
---|---|
Delivery프로젝트_5. (Trouble Shooting) JPA 연관관계 (단방향, 양방향) (0) | 2025.02.20 |
Delivery프로젝트_5.(Trouble Shooting)AOP/프록시객체/Lazy Loading (0) | 2025.02.18 |
Delivery프로젝트_4. HttpClient (feat. API_test) (0) | 2025.02.17 |
Delivery프로젝트_3. 어노테이션을 만든다고? (1) | 2025.02.14 |