Notice
Recent Posts
Recent Comments
Link
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 | 31 |
Tags
- MVC
- springsecurity
- 문제선택근거
- 이벤트핸들러this
- 로그백
- 이벤트핸들러등록
- OOP
- model
- Transaction
- 전전긍긍
- TDD
- DDD
- 냄새라도
- AOP
- 공통규약
- 클린아키텍처
- 커뮤니티서버
- 디스크i/o
- MVC요청플로우
- 사면초가
- JDBC
- Ajax
- SEQUENCE
- SpringLegacy
- 공통기술
- 설정세팅
- index
- JPA
- string
- 단위테스트
Archives
- Today
- Total
개발이군고구마
[냄새라도 TDD/OOP] 3. 통합테스트 (서비스) 본문
728x90
[OOP 설계 프로세스]
1. 설계 원칙 확립 < 구현 1단계
2-1. TDD 구현 단위(도메인) < 구현 2단계
2-2. TDD 구현 단위(컨트롤러-검증) < 구현 2단계
3. TDD 구현 통합
1. 서비스 통합테스트
🌐 환경설정
1) 통합테스트 DB 연결 설정과 뼈대 잡기
@TestConfiguration
public class MySqlTestContainerConfig {
@Bean
@ServiceConnection
public MySQLContainer<?> mySQLContainer() {
return new MySQLContainer<>("mysql:8.0");
}
}
- DB 로컬 연결은 컨테이너를 사용
-> 일일이 로컬에 테스트 환경 구축할 필요 없이 모든 팀원들과 같은 테스트 환경을 맞출 수 있음
@SpringBootTest(classes = {
GreenApplication.class,
MySqlTestContainerConfig.class,
TestBeanConfig.class
}, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ServiceIntegrationTest {
}
class InfoServiceIntTest extends ServiceIntegrationTest {
...
}
🌐 원칙
- 요청은 정상적으로 왔다는 가정 하에 (컨트롤러에서 검증함)
- 응답값이 정상적으로 반환되는지 확인함
2) 테스트 생각해보기 무엇이 필요할까
[조회]
1) 순서에 맞게 데이터가 뽑히는지 확인 (정렬)
2) 삭제나 미전시 표시 데이터는 조회가 되지 않아야 함
3) key 값은 제대로 들어왔으나 데이터로 조회가 되지 않는 경우 적절한 예외를 던지는지 확인
[등록/수정/삭제]
1) 요청 값은 정상적으로 들어왔으므로 제대로 등록/수정 되었는지만 확인
* 특히 Enum 값이 데이터에 들어갈 땐 Enum의 Code 값으로 들어가게 되는지
2) 삭제도 마찬가지
✅ 등록 테스트시에 MySql 로컬 저장은 컨테이너를 사용한다.
✅ 서비스단 파일의 bounded context 의 경우엔 @SpyBean 을 사용하여 호출시에 아무것도 하지 않게 stubbing 진행한다.
✅ 실제 서비스와 repository는 실제 Spring bean을 이용하기 때문에 @Autowired 사용
[조회]
🔵 테스트 코드
- Entity를 생성해서 delete 마킹하여 저장함
- 해당 정보는 제외하고 조회가 되어야 함
@Test
void 정보_목록을_조회할_때에는_삭제된_정보는_포함되지_않는다() {
// given
infoEntity.markDeleted();
infoRepository.save(infoEntity);
InfoRequest infoRequest2 = createSaveInfoRequest();
infoRepository.save(infoRequest2.toEntity());
int page = 0;
int size = 10;
// when
var response = infoService.getInfosForAdmin(page, size);
// then
assertThat(response.content()).size().isEqualTo(1);
}
🔴 실제 구현 코드
@Where(clause = "deleted = false AND is_display = 'Y'")
public class InfoEntity extends BaseEntity {
...
}
[등록]
🔵 테스트 코드
@MockitoSpyBean
private FileManager fileManager;
@Autowired
private JdbcTemplate jdbcTemplate;
@Nested
class 디폴트_Entity_생성하지_않음 {
@BeforeEach
void cleanUpBefore() {
jdbcTemplate.execute("TRUNCATE TABLE INFO");
}
@Test
void 정보를_등록한다() {
// given
InfoRequest saveRequest = createSaveInfoRequest();
Mockito.doNothing().when(fileManager).confirmUsingImage(Mockito.anyString());
// when
var response = infoService.saveInfo(saveRequest);
//then
assertThat(response.id()).isEqualTo("P000001");
assertThat(response.title()).isEqualTo(saveRequest.title());
assertThat(response.modifiedDate()).isNotNull(); // 수정일자 확인
}
}
- fileManager 에 있는 메서드를 접근할 떄 doNothing
- Test 실행할 때마다 Auto Increment 정보가 계속해서 남아 있어서 info table truncate 진행하고 통합 테스트
🔴 실제 구현 코드
@Override
public InfoDetailResponseByAdmin updateInfo(String infoId, InfoRequest updateRequest) {
InfoEntity infoEntity = getInfoEntity(infoId);
if (StringUtils.isNotEmpty(infoEntity.getImageUrl())) {
fileManager.unUseImage(infoEntity.getImageUrl());
}
log.info("[InfoServiceImpl] 정보공유 수정합니다. 정보공유 번호: {}", infoEntity.getId());
makeUpdateEntity(updateRequest, infoEntity);
fileManager.confirmUsingImage(updateRequest.imageUrl());
return InfoDetailResponseByAdmin.from(infoEntity);
}
2. 과연 통합테스가 꼭 필요한 것일까?
처음 의도한 바에 따르면 JPA Hibernate에서 받아오는 응답값이 제대로 도출되나 였다.
- 순서에 맞게 도출되나
- 도출된 값은 응답 Wrapper class에 제대로 매핑 되나
그러나 생각해보면 응답값에 대한 검증은 Entity 클래스만 만들어주고 stubbing을 하게 되면 충분히 단위 테스트 로도 검증을 할 수 있다는 것이다.
현재 구조상 서비스에 다양한 로직이 들어가는 것이 아니고 단순 CRUD 이기 때문에 통합테스트에서 걸리는 시간적인 비용을 생각하면 단위테스트로 변경도 생각할 수 있을 것 같다.
'SERVER > Architecture' 카테고리의 다른 글
| [MSA] 공통으로 사용하는 컴포넌트의 API 설계 방법 (1) | 2025.07.07 |
|---|---|
| [냄새라도 TDD/OOP] 2-2. 단위 테스트 (컨트롤러) (0) | 2025.07.04 |
| [냄새라도 TDD/OOP] 2-1. 단위 테스트 (도메인단) (0) | 2025.06.27 |
| [냄새라도 TDD/OOP] 1. 구현 원칙 확립 (0) | 2025.06.27 |
| [사면초가 TDD/OOP] 3. 통합테스트 (config mock의 주입) (0) | 2025.06.08 |