안녕하세요 선생님 질문이 생겨서 글 남깁니다.! 선생님이 생각하시는 가장 이상적인 테스트 코드 범위는 어디 까지인가요?? 예를들어 controller,service,repository가 있다고 가정하고 선생님 강의처럼 각각 레이어별로 테코를 짜고 service 쪽도 repo를 mock처리하여 단위테스트까지도 진행 하여야하나요? 제 질문을 정리하자면 controller,service,repository 각각 단위테스트 작성후 service + repo로 통합테스트 하는게 옳은 이상적인 범위인가 궁금합니다 감사합니다
private List<Product> findProductsBy(List<String> productNumbers) { return productRepository.findAllByProductNumberIn(productNumbers); } 그냥 이렇게 바로 리턴 해도 되지 않나요??
두 가지 궁금증이 생겨 질문드립니다!! Mock과 Stub에서 혼란을 느끼는지? Mock & Stub 에 대한 이해를 하였는지? // OrderTest.java import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; public class OrderTest { @Test void 주문_총_금액을_계산한다() { Product apple = new Product("사과", 1000); Product banana = new Product("바나나", 2000); Order order = new Order(); order.addProduct(apple); order.addProduct(banana); assertEquals(3000, order.totalPrice()); } } // OrderTest.java import org.junit.jupiter.api.Test; import static org.mockito.Mockito.*; import static org.junit.jupiter.api.Assertions.*; public class OrderTest { @Test void 주문_총_금액을_계산한다_Product_Mock_사용() { Product apple = mock(Product.class); Product banana = mock(Product.class); when(apple.getPrice()).thenReturn(1000); when(banana.getPrice()).thenReturn(2000); Order order = new Order(); order.addProduct(apple); order.addProduct(banana); assertEquals(3000, order.totalPrice()); } } 두 테스트에서 생각을 정리하다 보니 저는 마틴 파울러의 test double의 stub 과 mock 에 대한 개념보다 TTD or BDD 스타일의 테스트 코드를 작성하다가 테스트의 편의성을 위해 Mokito을 통한 Mock 테스트 를 먼저 접하게 된 후 마틴 파울러의 test double의 개념을 접하게 되었습니다. 마틴 파울러의 Mock 과 Stub 테스트에서 혼란스러운 이유 테스트 코드를 작성하다보면 자연스럽게 then에 해당하는 부분 은 대부분 상태(값)을 검증하는 테스트 를 위주로 테스트 코드를 작성하게 되는거 같습니다. 주문_총_금액을_계산한다() -> 단위 테스트로 상태(값)을 검증 주문_총_금액을_계산한다_Product_Mock_사용() -> 나는 Mockito의 mock 테스트 로 상태(값)을 검증 test double stub -> 상태(값) 검증 mock -> 행동 검증 위 와 같이 상태(값) 을 검증하는 테스트를 하다보니 stub 테스트 와 Mockito 의 mock을 사용한 테스트 을 동일시 보게 된거같습니다. Mockito의 mock 테스트 의 개념과 test double의 mock 테스트 가 동일한 개념이 아니다. Mokito의 mock을 사용하여 상태(값)을 검증하는 테스트를 하다보니 test double의 stub 테스트 를 Mock 테스트 하였다라고 생각한게 아닌가? 테스트 케이스 stub 과 mock 테스트 구별하기 주문_총_금액을_계산한다() 해당 테스트는 실제 객체 를 이용하여 상태(값)에 대한 검증 을 한다. -> Stub 테스트 주문_총_금액을_계산한다_Product_Mock_사용() 해당 테스트는 Product 를 Mock 하여, order의 totalPrice 을 검증 한다. -> Mock?, Stub? 주문_총_금액을_계산한다_Product_Mock_사용()를 어떻게 바라 볼 것인가? test double의 mock 테스트를 준비 하였지만 검증 부분을 잘못하였다. test double의 stub 테스트를 실제 객체가아닌 가짜객체로 테스트 하였다. @Test void 주문_총_금액을_계산한다_Product_Mock_사용() { // given Product apple = mock(Product.class); Product banana = mock(Product.class); when(apple.getPrice()).thenReturn(1000); when(banana.getPrice()).thenReturn(2000); Order order = new Order(); order.addProduct(apple); order.addProduct(banana); // when int total = order.totalPrice(); // then verify(apple).getPrice(); // apple.getPrice()가 호출되었는지 검증 verify(banana).getPrice(); // banana.getPrice()가 호출되었는지 검증 } 주문_총_금액을_계산한다_Product_Mock_사용()를 위와 같이 수정한다면 test double에서 이야기하는 mock 테스트이지 않을까 생각이 듭니다. 물론 모든 테스트를 test double의 Mock, Stub 테스트라는 틀에 맞춰 작성해한다는 아니라고 생각이 듭니다. 주문_총_금액을_계산한다_Product_Mock_사용()가 마틴 파울러가 이야기하는 test double의 개념 에서 어떻게 생각해 볼 것인가? 에대한 궁금증 우빈님의 생각이 궁금 하기도 합니다. 제가 잘 이해를 하였는지 확인이 필요하여 질문드립니다.
- 학습 관련 질문을 남겨주세요. 상세히 작성하면 더 좋아요! - 먼저 유사한 질문이 있었는지 검색해보세요. - 서로 예의를 지키며 존중하는 문화를 만들어가요. - 잠깐! 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요. 안녕하세요, 리액트 학습 중에 궁금한 게 있어서 질문드려요. 기본 path 가 아닌 라우트로 바로 접속을 할때, html을 받으면 라우팅이 이뤄지구 useeffect로 api 요청이 가면서 페이지 렌더링이 이뤄지는건가요?
안녕하세요, 6:13초 쯤에 Mock에 대한 예시로 앞에서 작성했던 "when 해서 sendMail 했을 때 파라미터 4개를 넘겨줬을 때 어떤 값을 리턴함" 을 말씀해주셨는데요, 이게 stub에 가까운게 아닌가란 의문이 들었습니다. 마틴 파울러의 글을 보면 Mock은 결국 호출 여부나 호출 횟수 같은 것을 검증하는 것 같더라구요. 뭔가 이해가 잘 안돼서 질문을 남깁니다. 감사합니다.
안녕하세요~ 강의가 넘 좋습니다!! 넘 재밌게 잘 들었어요~ msa프로젝트에서 kafka, feignClient는 테스트를 어떻게 진행하는지 궁금합니다. (서비스단 테스트시 카프카를 mocking해서 테스트하는 것을 여쭙는게 아닙니당 ㅎㅎ) 저 자체는 테스트하지 않으시는지..? 실제로는 서비스 여러개 띄워놓고 메세지 발행이나 api 통신이 되는지 확인하고 있는데 테스트로도 할 수 있는 방법이 있나해서요. 현업에서는 어떻게 하는지 궁금합니다~
@Test void calculateTotalPrice() { 위에 이미지 처럼 테스트에서 검증 후 , 아래처럼 public int calculateTotalPrice() { return beverages.stream().mapToInt(Beverage::getPrice).sum(); } 로 리팩토링 진행하셨는데 테스트코드에서 검증 통과받으면, 프로젝트 코드에서 리팩토링 된 부분은 천하무적 이라고 생각하면 되나요 ? 이때 발생하게 되는 변수는 없을련지,,,, 궁금합니다!
안녕하세요 테스트 케이스 세분화하기 강의에서 // CafeKiosk.java public void add(Beverage beverage, int count) { if (count < 0) { throw new IllegalArgumentException("음료는 1잔 이상 주문하실 수 있습니다."); } for (int i = 0; i < count; i++) { beverages.add(beverage); } } 해당 메소드에 대한 단위테스트를 만드는데 궁금증이 생겨서 질문을 남겨봅니다. [질문] 경계값(0, -1)에 대해서도 테스트 케이스를 세분화해서 만들어야 되는건가? // 예시 // 경계값 0에 대한 테스트 함수 @Test void addZeroBeverages() { ... (생략) } // 경계값 -1에 대한 테스트 함수 @Test void addNegativeOneBeverages() { ... (생략) } 아니면 테스트 케이스 하나를 만들어서 그안에 값을 변경하면서 테스트해야되는건지 궁급합니다! // 예시 @Test void addZeroAndNegativeOneBeverages() { ... (생략) // 0값 체크 후 테스트 통과하면 1로 고쳐서 테스트 assertThatThrownBy(() -> cafeKiosk.add(americano, 0)) .isInstanceOf(IllegalAccessError.class) .hasMessage("음료는 1잔 이상 주문하실 수 있습니다."); }
학습 관련 질문을 남겨주세요. 어떤 부분이 고민인지, 무엇이 문제인지 상세히 작성하면 더 좋아요! 먼저 유사한 질문이 있었는지 검색해 보세요. 서로 예의를 지키며 존중하는 문화를 만들어가요. 안녕하세요 테스트 코드에 대해 고민이있는 학생입니다. 단위테스트에 대한 질문을 하고싶습니다! 저는 단위테스트를 h2를 이용하지않고 fake repo를 만들어서 테스트하였고 통합테스트할때는 h2를 이용해서 테스트하였는데 테스트 강의하시면서 강조하셧던 부분이 테스트하는것도 비용이다! 그래서 스프링서버를 띄우는것을 extends해서 통합하여 관리하셨던것이 굉장히 큰 도움이되었습니다. 그렇다면 단위테스트할때 fake Repo를 커스텀하는 노력만 조금 들인다면 단위 테스트에대한 속도도 더빨라질거라 생각하는데 어떻게 생각하시나요? fake repo를 만들때 list, map, set 중에 저는 map을선택하였는데 혹시 어떤것을 사용하시는지? 테스트 패키지 구조를 통합, 단위 라는 패키지를따로만들어서 넣는것보다 alt + insert로 만들기 쉽게 같은공간에 단위,통합이 존재하게하면서 Test, IT 라는 네이밍으로 클래스를만들고있는데 ex) OrderServiceTest, OrderServiceIT 우빈님은 어떻게 통합과 단위 패키지구조를 설계하시는지 궁금합니다.
학습 관련 질문을 남겨주세요. 어떤 부분이 고민인지, 무엇이 문제인지 상세히 작성하면 더 좋아요! 먼저 유사한 질문이 있었는지 검색해 보세요. 서로 예의를 지키며 존중하는 문화를 만들어가요. 안녕하세요. 개인 프로젝트를 진행하다 궁금한 점이 있어 질문드립니다. 현재, 서로 다른 두 트랜잭션이 동시에 실행될 때 발생할 수 있는 동시성 문제를 해결하기 위해 락을 적용해두었습니다. 테스트 코드에서는 락이 없는 경우에는 문제 상황이 발생하고, 락 적용 시에는 정상 작동함을 보여주고자 합니다. 인터넷에서 관련 자료를 찾아본 결과, Executor 와 CountDownLatch 를 활용하여 동시성 문제를 테스트하는 방식을 많이 사용하길래, 저도 이를 적용해 테스트를 구성하려 하고 있습니다. 멀티스레딩 환경에서는 실행 순서가 보장되지 않으므로, 운이 나쁘면 두 트랜잭션이 순차적으로 실행될 수도 있다고 생각했고 실행 순서를 제어하여 문제가 발생할 수 있는 상황을 코드로 만들어야 한다고 생각했습니다. 그러나 Executor 와 CountDownLatch 만으로는 트랜잭션 단위로 묶인 작업들의 실행 순서를 제어하기 어려워, 원하는 동시성 문제 시나리오를 재현하는 데 한계가 있었습니다. wait , notify 등의 스레드 제어 메서드를 활용하면 원하는 실행 순서를 만들 수 있을 것 같지만, 이것이 최선의 방법인지 고민이 되어 질문드립니다. 선생님께서는 동시성 문제 테스트를 진행할 때, 실행 순서를 제어하여 문제가 발생하는 시나리오를 인위적으로 만들고 이런 상황에서도 정상 작동함을 확인하시나요? 아니라면 동시성 문제를 테스트할 때 어떤 방법을 사용하시는지 궁금합니다
학습 관련 질문을 남겨주세요. 어떤 부분이 고민인지, 무엇이 문제인지 상세히 작성하면 더 좋아요! 먼저 유사한 질문이 있었는지 검색해 보세요. 서로 예의를 지키며 존중하는 문화를 만들어가요. 안녕하세요 강의를 보다가 OrderRepository에 대한 테스트는 직접 짜보라는 말씀을 듣고 직접 한번 작성을 해봤습니다. @ActiveProfiles("test") @DataJpaTest class OrderRepositoryTest { @Autowired private OrderRepository orderRepository; @Autowired private ProductRepository productRepository; @DisplayName("원하는 주문 상태를 가진 주문들을 조회한다.") @Test void findOrdersBy() { // 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)); Order order1 = createOrderWithOrderStatus(List.of(product1, product2), LocalDateTime.now(), PAYMENT_COMPLETED); Order order2 = createOrderWithOrderStatus(List.of(product3), LocalDateTime.now().minusDays(1), PAYMENT_COMPLETED); Order order3 = createOrderWithOrderStatus(List.of(product1, product2, product3), LocalDateTime.now().plusDays(1), PAYMENT_COMPLETED); orderRepository.saveAll(List.of(order1, order2, order3)); // when List<Order> orders = orderRepository.findOrdersBy( LocalDateTime.now().minusDays(1), LocalDateTime.now().plusDays(1), PAYMENT_COMPLETED ); // then assertThat(orders).hasSize(2) .extracting("orderStatus", "totalPrice", "registeredDateTime") .containsExactlyInAnyOrder( tuple(PAYMENT_COMPLETED, 8500, order1.getRegisteredDateTime()), tuple(PAYMENT_COMPLETED, 15500, order3.getRegisteredDateTime()) ); } private Order createOrder(List<Product> products, LocalDateTime registeredDateTime) { return Order.create(products, registeredDateTime); } private Order createOrderWithOrderStatus(List<Product> products, LocalDateTime registeredDateTime, OrderStatus status) { Order order = createOrder(products, registeredDateTime); order.updateOrderStatus(status); return order; } private Product createProduct(String productNumber, ProductType productType, ProductSellingStatus sellingStatus, String name, int price) { return Product.builder() .productNumber(productNumber) .type(productType) .sellingStatus(sellingStatus) .name(name) .price(price) .build(); } } 이런 식으로 작성을 하였는데 제가 궁금한 점은 createProduct는 대부분의 테스트 클래스에서 사용되는데 하나의 static 클래스와 같이 외부로 분리하는게 좋지 않을까 라는 생각을 해봤습니다. 저는 Order 클래스의 상태 변경을 위해서 updateOrderStatus라는 메소드를 생성 하였는데 (아직 해당 강의를 끝까지는 보지 않고 강의를 보기전에 먼저 테스트 코드를 작성 해보고 있습니다) 이런 식으로 테스트를 위해서 메소드를 생성 하는 경우도 괜찮을까요? 제가 작성한 코드의 given 절이 너무 길어지는 느낌을 받았습니다. 현재는 order와 product를 생성 하는 정도이지만 더 복잡한 로직을 생성 할 경우 given이 너무 길어 지는것에 대한 해결 방안이 있을까요? 최대한 구글을 사용하여 찾아는 보았지만 우빈님의 생각도 궁금하여 여쭤보았습니다! 긴 글 읽어주셔서 감사합니다 🙂
학습 관련 질문을 남겨주세요. 어떤 부분이 고민인지, 무엇이 문제인지 상세히 작성하면 더 좋아요! 먼저 유사한 질문이 있었는지 검색해 보세요. 서로 예의를 지키며 존중하는 문화를 만들어가요. 테스트 환경이 다르면 스프링을 새로 띄워야 된다고 하셨는데, 여기서 테스트 환경이란게 테스트 클래스마다 의존 관계를 주입 받는 클래스가 하나라도 달라지면 스프링이 새로 뜨게 되는 건가요??
안녕하세요. 하위 레이어가 상위 레이어를 알고 있는 경우는 좋지 않다고 하셨는데 만약에 도메인을 순수하게 유지하고 싶어서 도메인 엔티티와 jpa 엔티티를 분리하고, service와 jpaRepository에 DIP를 적용하게 되면 service에서는 repository 인터페이스를 사용하고, repository 인터페이스를 구현하는 repositoryImpl에서 jpaRepository를 사용하게 될 것 같습니다. 이 경우에 repositoryImpl에서는 jpa 엔티티 -> 도메인 엔티티로 변환을 하고, 도메인 엔티티를 service로 반환해주어야 할 것 같은데 이렇게 되면 하위 레이어인 infrastructure(persistance) 레이어에서 상위 레이어에 있는 도메인 엔티티에 대한 의존성이 생긴다고 생각하는데 이런 경우는 상위 레이어에 의존성이 생겨도 상관 없는 경우인가요?? 테스트 강의와 무관한 질문을 드려서 죄송합니다 ㅠㅠ