[워밍업 클럽 스터디 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"));
}
댓글을 작성해보세요.