[워밍업 클럽 스터디 2기 - BE] (클린코드, 테스트코드) day 15 미션
출처 : 인프런 워밍업 클럽 스터디 2기 - 백엔드 클린코드, 테스트 코드(Java, Spring Boot)
Layered Architecture

핵심은 관심사의 분리. 역할을 나누어 유지보수성을 늘린다.
Persistence Layer
데이터와 DB가 만나는 계층
JPA, MyBatis 등등 결국 최종 쿼리를 DB로 전달해야 함.
이 계층은 DB에 쿼리를 전달하는 역할만 해야지 여기서 뭔가 데이터를 가공한 후에 쿼리를 만들면 안됨.
데이터 가공은 비즈니스 계층에서 하는 것이 바람직하다고 생각.
테스트 시 스프링 컨테이너를 사용하지만 쿼리 자체를 테스트하는 것이기 때문에 단위 테스트 성격을 띈다.
테스트 방법
@SpringBootTest를 사용 시@Transaction이 없기 때문에tearDown()을 구현@DataJpaTest사용 시@Transaction이 있기 때문에 이에 따른 트랜잭션 전파를 잘 고려해서 판단해야 함.이걸 고려하지 않고 테스트 작성 시 테스트는 잘 통과하는데 실제로는 안될 수도 있음.
테스트 시 샘플 객체를 만들어서 저장 후 (`given`) 테스트할 메서드를 호출 (`when`)
검증(`then`)시 단일 객체라면 그냥 비교하면 되는데, List같은 자료구조에 담긴 여러 객체라면
1. 먼저 사이즈를 체크
2.
extracting()으로 검사할 항목 체크3.
contains*()+tuple(데이터)로 비교
@Test
@DisplayName("원하는 판매상태를 가진 상품들을 조회한다.")
void findAllBySellingStatusIn() {
// given
Product product1 = createProduct("001", HANDMADE, SELLING, "아메리카노", 4000);
Product product2 = createProduct("002", HANDMADE, HOLD, "카페라떼", 4500);
Product product3 = createProduct("003", HANDMADE, STOP_SELLING, "팥빙수", 7000);
productRepository.saveAll(List.of(product1, product2, product3));
// when
List<Product> products = productRepository.findAllBySellingStatusIn(List.of(SELLING, HOLD));
// then
assertThat(products).hasSize(2)
.extracting("productNumber", "name", "sellingStatus")
.containsExactlyInAnyOrder(
tuple("001", "아메리카노", SELLING),
tuple("002", "카페라떼", HOLD)
);
}
Business Layer
핵심적인 비즈니스 로직을 처리하는 계층
Persistence Layer와 상호작용한다.트랜잭션이 보장되어야 한다.
테스트 방법
Persistence Layer를 묶어서 통합 테스트Persistence Layer를Mocking해서 단위 테스트Business Layer는 트랜잭션이 중요하기 때문에 테스트시에도 이에 따른 트랜잭션 전파를 잘 고려해서 판단 해야 함.
Presentation Layer
클라이언트로부터 데이터를 받아
Business Layer로 요청을 넘기는 계층데이터에 대한 기본적인 검증을 수행 (필수 입력 값, 데이터 타입, null 등)
Business Layer로부터 받은 데이터를 사용자에게 반환Business Layer가Presentation Layer를 모르도록 설계하면 좋다.Business Layer에 클라이언트에게 받은 데이터를 바로 넘기는 것 보다 서비스 전용 DTO로 래핑해서 넘기기.
클라이언트에게 값을 넘겨줄 때는 정해진 포맷(errorCode, message, data 등)으로 넘겨주는 것이 좋다.
테스트 방법
데이터에 대한 기본적인 검증을 수행
Business Layer는 Mocking해서 테스트 진행mockMvc를 활용하여 Http 응답에 대한 검증을 진행한다.
@Test
@DisplayName("신규 주문을 등록한다.")
void createOrder() throws Exception {
// given
OrderCreateRequest request = OrderCreateRequest.builder()
.productNumbers(List.of("001"))
.build();
// when // then
mockMvc.perform(post("/api/v1/orders/new")
.content(objectMapper.writeValueAsString(request))
.contentType(MediaType.APPLICATION_JSON)
)
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value("200"))
.andExpect(jsonPath("$.status").value("OK"))
.andExpect(jsonPath("$.message").value("OK"));
}
댓글을 작성해보세요.