묻고 답해요
141만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결Practical Testing: 실용적인 테스트 가이드
강의에서 나온 Service 레이어 테스트에 대해서 질문이 있습니당
강사님은 classicist를 지향한다고 했는데, classicist의 단위 테스트는 데이터베이스와 같은 공유 의존성을 테스트 대역(mock)으로 대체해야 한다는 것으로 알고 있는데 유연한 사고(?)로 classist를 지향하지만 통합 테스트로 단위를 확인하는 것을 좋아한다 정도로 정리하면 좋을까요? 아니면 H2를 사용했으니 테스트 대역을 운영환경보단 빠른 환경으로 교체했으니 여전히 단위 테스트라고 생각을 하시는 걸까요?service 레이어를 통합 테스트로 안 짜는 팀에서 classicist를 지향한다면 DB에 대한 의존성을 어떻게 대체하는지 간단한 예제라도 보여주실 수 있을까요? ㅠ
-
미해결Practical Testing: 실용적인 테스트 가이드
Mock과 Stub의 차이가 아직 잘 구분되지 않습니다.
개인적으로 Mock은 상태를 (내가 작성한 값을) 반환하는 것, Stub은 상태가 (내가 작성한 구현대로) 반환되는 것이라고 정의를 내리고 있습니다. 이와 같은 표현으로 차이를 이해하고 있어도 괜찮을까요?아니면 좀 더 명확한 표현이 있을까요? Mock 객체에 우리가 원하는 행위를 정의(Stubbing)하면,그 객체는 이제 Mock 객체라고 해야하나요 아니면 Stub 객체라고 해야하나요? 아래 코드를 보고 Mock 객체인 mailSendClient의 send 메서드에 대한 Stubbing이 이루어졌다. 라고 하면 맞는 표현인가요?@ExtendWith(MockitoExtension.class) class MailServiceTest { @Mock private MailSendClient mailSendClient; @Mock private MailSendHistoryRepository mailSendHistoryRepository; @InjectMocks private MailService mailService; @DisplayName("메일 전송 테스트") @Test void sendMail() { // given when(mailSendClient.send(anyString(), anyString(), anyString(), anyString())) .thenReturn(true); // when boolean result = mailService.sendMail("", "", "", ""); // then assertThat(result).isTrue(); // verify(mailSendHistoryRepository, times(1)) // .save(any(MailSendHistory.class)); } } mailSendClient는 Mocking하더라도 상태검증을 할 수도 있고, Stubbing하더라도 행위검증을 할 수도 있지 않나요?이 둘의 차이는 Mockist와 Classicist의 차이이지, Mock과 Stub의 차이라고 할 수 없지 않을까요?
-
미해결Practical Testing: 실용적인 테스트 가이드
@SpringBootTest 여러 개 사용 시 데이터 남아있는지 여부 문의
안녕하세요. 프로젝트 내에서 통합 테스트를 위해서 @SpringBootTest를 사용한 여러 테스트 코드를 작성하였습니다.예를 들어 아래와 같이 별개의 2개 테스트 코드에는 각각 school table의 값을 생성 및 조회하는 코드가 들어있습니다.@SpringBootTest @Transactional class aTest { // JPA 사용해서 school Table의 data 생성/조회 } --------------------- @SpringBootTest @Transactional class bTest { // JPA 사용해서 school Table의 data 생성/조회 } 위와 같이 테스트 코드를 작성하고 aTest.java 에서 school table에 3개의 데이터를 넣은 경우, bTest.java 에서 school 테이블의 값을 읽었을 때, aTest.java에서 넣은 값은 지워지는 것인지요? 아니면 그대로 남아 bTest.java 에서 조회가 가능한 것인지요?@Transactional이 들어가 있는 경우 데이터가 rollback 되는 것으로 알고 있어서, bTest.java에서 읽었을 때는 빈 테이블이 있을 것으로 예상했는데, 테스트를 해보면 School table의 id 값(자동 증가하는 값)이 bTest.java에서 처음 읽었을 때 4로 보이는 현상이 있어서 질문을 드립니다.
-
미해결Practical Testing: 실용적인 테스트 가이드
강사님 유효성 검증의 분리에 대해 궁금한점이 있습니다.
학습 관련 질문을 남겨주세요. 어떤 부분이 고민인지, 무엇이 문제인지 상세히 작성하면 더 좋아요!먼저 유사한 질문이 있었는지 검색해 보세요.서로 예의를 지키며 존중하는 문화를 만들어가요. 저희만의 서비스에 특화된 비즈니스 검증로직은 따로 서비스나 도메인에서 분리해서 검증하라고 하셨는데, 정확히 이해가 가지 않아서 좀더 질문 드립니다.예를 들어, 패스워드 검증 로직이 있다고 하면, 저희만의 서비스는 패스워드 검증을( 영문,특문 혼합 8자이상)이라고 정했을때, 컨트롤러의 요청 부분에서는 @NotBlank를 처리하고, 서비스나 도메인에서 위의 패스워드 검증 로직을 진행하라는 말씀인가요??
-
미해결Practical Testing: 실용적인 테스트 가이드
환경별 DB 분리 질문있습니다.
안녕하세요.application.yml에서 local, test로 환경을 나누어서 h2 db를 통해 테스트 코드를 만들어보고있습니다.나중에 운영 서버나 릴리즈 서버같은곳에 빌드, 배포할때도 테스트를 진행해야 할텐데 DB 주소를 어떻게 설정하는게 좋을까요? 각각 실제로 사용할 DB의 주소를 적어놓을텐데 빌드하면서 테스트를 진행하면 해당 DB의 테이블과 데이터를 건드리게 될거란 생각이 들어서요..테스트용 resources로 yml을 따로 작성해서 해야할까요?
-
미해결Practical Testing: 실용적인 테스트 가이드
이 강의 주제를 듣다가.. 외부 시스템
학습 관련 질문을 남겨주세요. 어떤 부분이 고민인지, 무엇이 문제인지 상세히 작성하면 더 좋아요!먼저 유사한 질문이 있었는지 검색해 보세요.서로 예의를 지키며 존중하는 문화를 만들어가요. 제가 테스트 코드를 수행 도중에 맞닥뜨린 고민이 있었습니다.보통 메일과 같은 외부시스템(AWS)를 사용하게 되면, 그 해당 외부시스템의 key값과, secret값이 필요해서 application.yml 에 해당 key값과 secret값을 환경 변수로 설정해서, @Value 애노테이션을 통해서 설정 클래스를 만드는데, 이때테스트 코드를 만들게 되면 application-test.yml과 같은 테스트 전용 yml 파일을 만들어서 테스트 환경을 실행시키도록 하게 되는데, 강사님은 이럴때 어떻게 처리하시는지 궁금합니다. application-test 파일에 설정을 안하게 되면 런타임시 에러가 발생하게 되어, 실행시 에러가 나는데. 외부 시스템은 Mocking 처리하도록 하니. 테스트용 임시로 임의값을 key, secret 값으로 설정하면 되는건지 궁금하네요 진짜 좋은 강의 감사합니다 ^^ ㅎㅎ
-
미해결Practical Testing: 실용적인 테스트 가이드
Order.class 에대하여
학습 관련 질문을 남겨주세요. 어떤 부분이 고민인지, 무엇이 문제인지 상세히 작성하면 더 좋아요!먼저 유사한 질문이 있었는지 검색해 보세요.서로 예의를 지키며 존중하는 문화를 만들어가요. 강의 듣다가 궁금한 점이 생겨서 질문을 작성합니다!Order 클래스는 @Entity 어노테이션이 붙어있는 도메인 객체인데 여기에 어떤 로직? 이라고 해야 하나요? List<OrderProduct> 를 바로 넣어주는 것이 아니고 List<Product>를 받아서 값을 뽑아서 new OrderProduct 로 생성하거나, calculateTotalPrice() 같은 메서드를 작성해도 괜찮은가요? (현업에서도 많이 쓰이는 방법인가요? 뭔가 따로 service로 빼도 괜찮지 않을까 라는 생각이 들었습니다. )전에 간단한 토이 프로젝트를 할 때 cascade = CascadeType.ALL 가 아닌 cascade = CascadeType.REMOVE 로 설정하고 Order을 생성한 후 OrderProduct를 수동으로 생성했었는데 CascadeType.ALL 이 더 범용적으로 많이 쓰는 방법인지테스트 코드 작성법을 배우려고 듣게 되었는데 뭔가 라이브 코딩을 따라가면서 전반적으로? 많이 배우고 있어서 좋아요! ㅎㅎ 좋은 강의 감사합니다.
-
미해결Practical Testing: 실용적인 테스트 가이드
테스트 질문 드립니다
1 서비스에서 체크후 예외를 던지는것과 stock에서 체크후 예외를 던지는 것은 다르다라는것이 잘 이해갸 되지 않습니다강의 내용을 보아도 잘 이해가 되지 않아 굳이 이렇게 나눠야하는지에 대해 다시한번 설명 부탁드립니다 ㅜㅜ 또한 저는 stock 객체 자체에 판단할 수있는게 좋지 않나? 즉 stock의 책임이지 않을까?란 생각을 통해service에서 stock 객체가 할일을 가지고 가면 응집도 면에서 좋지 않다라는 생각을 가지고 있는데요 이에 대해 어떻게 생각하시나요?public boolean isQuantityLessThan(int quantity) { return this.quantity < quantity; } public void deductQuantity(int quantity) { if (isQuantityLessThan(quantity)) { throw new IllegalArgumentException("차감할 재고 수량이 없습니다."); } this.quantity -= quantity; } 2 테스트를 위한 코드강의 내용 중 OrderCreateRequest와 관련되어 테스트를 위한 생성자가 생성되었다고 생각을 하는데요 테스트를 위한 코드에 대해 어떻게 생각하시는지 궁금합니다 저는 테스트를 위한 코드를 생성하면 안된다라고 생각하는데요이유는 테스트를 위한 것으로 만들어 놨지만 다른곳에서 사용하게 되는 같은 현상이 발생될 수 있다라고 생각기때문입니다. OrderCreateRequest request = OrderCreateRequest.builder() .productNumbers(List.of("001", "002")) .build(); 3 어떤걸 어디서부터 어디까지 테스트해야되나?라는 의문입니다.OrderService를 구현하면서 Order와 관련된 테스트(Order.create()등등)를 진행하였고 또한 주문을 통해 생성된 OrderResponse에 대한 테스트를 진행하는 것을 보았습니다. 아래는 Order와 관련된 코드입니다public Order(List<Product> products, LocalDateTime registeredDateTime) { this.orderStatus = OrderStatus.INIT; this.totalPrice = calculateTotalPrice(products); this.registeredDateTime = registeredDateTime; this.orderProducts = products.stream() .map(product -> new OrderProduct(this, product)) .collect(Collectors.toList()); } public static Order create(List<Product> products, LocalDateTime registeredDateTime) { return new Order(products, registeredDateTime); } private int calculateTotalPrice(List<Product> products) { return products.stream() .mapToInt(Product::getPrice) .sum(); }이 코드에서 아래에 해당하는 코드만 테스트를 진행하셨는데요this.orderStatus = OrderStatus.INIT; this.totalPrice = calculateTotalPrice(products); this.registeredDateTime = registeredDateTime;만약에 위와 같은 컬럼이 끝이아니라 아래와 같이 수많은 컬럼이 있다면 이 모든것들을 다 테스트 해주어야 하는걸까요?제 의문은 어떤것은 테스트하고 어떤것은 테스트 하지 말아야하나? 모든것들 전부 테스트 해야하나? 어디서부터 어디까지 테스트를 해야되지라는 정답이 없는 고민이 있어 강사님의 의견을 여쭙고자 질문을 드려봅니다this.orderStatus = OrderStatus.INIT; this.totalPrice = calculateTotalPrice(products); this.registeredDateTime = registeredDateTime; ... ... ... ... ... ...은 컬럼을 의미합니다 아무거나 다른 컬럼을 생각해주셔도 좋습니다!어떠한 한가지만을 테스트할때는 강사님이 말씀해주신것처럼 경계값이든 무엇이든 기준을 가지고 테스트를 진행하면 될텐데 OrderResponse나 그런것들 즉 어떤것에 의해 생성된것에 대해 테스트를 해야할때 어디서부터 어디까지 테스트를 해야하는지 잘모르겠습니다.혹시 가능하시다면 어떤기준을 가지고 어떤것에 대해 테스트를 하시는지 어디서부터 어디까지 테스트를 해야하는지에 대한 생각을 가지고 있으시다면 말씀부탁드립니다 4 OrderService에 대한 테스트를 어디서부터 어디까지 어느정도 테스트해야하나입니다 지금은 아래와 같은 것들을 테스트 해주고 있는데요assertThat(orderResponse.getId()).isNotNull(); assertThat(orderResponse) .extracting("registeredDateTime", "totalPrice") .contains(registeredDateTime, 4000); assertThat(orderResponse.getProducts()).hasSize(2) .extracting("productNumber", "price") .containsExactlyInAnyOrder( tuple("001", 1000), tuple("002", 3000) ); 만약 Order Service에 대한 로직이 복잡해지고 결제나 뭐 등등 이런것들이 증가가 된다면 그런부분들도 모두 test에서 검증을 해줘야하는걸까요? 만약 모두 그런부분들을 검증해준다고하면 구현부분이 조금 바뀐다고해도 테스트가 쉽게 깨질 수 있다라고 생각하는데요 그래서 생성이라면 생성과 관련된 테스트만 잘 테스트를 해야하는것인가 아니면 내부 구현에 대한 테스트도 진행을 해야하는것인가에 대한 의문이 있는데 강사님은 어떻게 생각하시나 궁금하여 질문을 드려봅니 5 앞에서 한 테스트를 뒤에서도 검증 해야하나? 라는 의문입니다 stock에 대한 단위테스트를 진행하면서 감소 메서드에 대한 테스트도 진행하였습니다.그런데 OrderService에서 아래와 같은 코드로 검증을 해주는데요List<Stock> stocks = stockRepository.findAll(); assertThat(stocks).hasSize(2) .extracting("productNumber", "quantity") .containsExactlyInAnyOrder( tuple("001", 0), tuple("002", 1) ); 항상 의문이었던 것은 다른 곳에서 테스트를 통해 검증이 끝난것도 다시 테스트를 해줘야하나라는 의문입니다.이 코드 말고도 이와 비슷한 현상이 발생할 수 있을거 같은데요다른 코드에서 검증을 진행해주는 코드를 앞 layer에서나 다른곳에서 쓸떄 또 검증을 해줘야하나라는 의문이 있습니다. 6 메일에관한 테스트는 하지않는걸까요?서비스테스트 중에 외부에 관한 즉 메일링 관련된 것은 목킹한것을 보았습니다. 그러면 메일에 관한 것은 단위테스트를 통해서 하는 것인지 아니면 그냥 잘동작하는것이라고 가정하고해야 하는걸까요? (그것을 테스트할 수 없기 때문에?)7 밸리드는 모두 테스트?프레젠테이션 레이어 즉 컨트롤러 테스트 중 밸리드에 관련된 부분은 보통 모두 테스트 하시는편인가요 아니면 정말 필요하다고 생각되시는 부분만 하시는지 궁금합니다. 모두 하면 모두 검증을하는거니 당연히좋겠지만 이것도 모두 비용이라 생각하는데 이런것들도 모두해야되나?라는 의문이 들어 질문드립니다 적다보니 의문이 많이 생겨 주절주절 적어봤는데요.. 좋은 테스트 코드를 지향하면서 테스트를 통해 깔끔하게 문서화하고 이를 통해 신뢰도 있는 프로그램을 만들지에 대한 고민을 하면서 정답이 없는 고민들에 대해 생각이 들었고 이 강의로 이끌리게 되었습니다 혹시나 제 질문이 잘이해가 되지 않으신다면 말씀부탁드립니다!
-
미해결Practical Testing: 실용적인 테스트 가이드
PK값으로 테스트 할 때 질문입니다.
학습 관련 질문을 남겨주세요. 어떤 부분이 고민인지, 무엇이 문제인지 상세히 작성하면 더 좋아요!먼저 유사한 질문이 있었는지 검색해 보세요.서로 예의를 지키며 존중하는 문화를 만들어가요. 우선, 덕분에 테스트 코드를 작성하는데 많은 도움이 됐습니다! 현재 토이프로젝트 진행 중에 PK 값을 Long 타입으로 두고 @GeneratedValue(strategy = GenerationType.IDENTITY) 이 전략을 사용하니, tearDown을 해도, 전체 테스트에서는 create하고 삭제를 하니, PK인 Long이 1L인 것을 보장을 못하더라구요.그래서 create하는 메서드의 반환을 void로 했다가 Long 타입으로 반환하는데, 이렇게 하는 것도 좋은 코드인가요..?@DisplayName("사용자를 조회하면 사용자의 이름, 직업, 전화번호, 성별을 조회한다.") @Test void getUserInfo() { //given Register register = Register.builder() .name("name") .job(Jobs.STUDENT) .phone("010-0000-0000") .man(true) .build(); Long id = usersService.registerUser(register); //when UserResponse userInfo = usersService.getUserInfo(id); // then assertThat(userInfo.name()).isEqualTo("name"); assertThat(userInfo.job()).isEqualTo(Jobs.STUDENT); assertThat(userInfo.phone()).isEqualTo("010-0000-0000"); assertThat(userInfo.man()).isEqualTo(true); } // 단건 조회 public UserResponse getUserInfo (Long id){ Users user = usersRepository.findById(id). orElseThrow(IllegalArgumentException::new); return UserResponse.of(user); } 입니다!
-
미해결Practical Testing: 실용적인 테스트 가이드
실무에서 연관관계 매핑에 대해 질문이 있습니다.
학습 관련 질문을 남겨주세요. 어떤 부분이 고민인지, 무엇이 문제인지 상세히 작성하면 더 좋아요!먼저 유사한 질문이 있었는지 검색해 보세요.서로 예의를 지키며 존중하는 문화를 만들어가요. 실무에서는 연관관계 매핑이 하나의 애그리게이트에서는 사용하되 다른 에그리게이트와 매핑시에는 엔티티 매핑이 아닌다른 에그리게이트의 id로 매핑한다고 하더라구요. 엔티티 매핑이 ManyToOne 관계에서 Many쪽에 one의 id로 매핑을 매핑하면 되는데, ManyToMany 관계에서의 다른 애그리게이트 관계와의 연관 관계 매핑이 아닌 id로는 어떻게 해결하시는 궁금합니다.
-
미해결Practical Testing: 실용적인 테스트 가이드
Spy 질문입니다.
안녕하세요 강사님.강사님 강의를 듣고 실무에 적용을 해보고 있습니다.예를 들어 A Service 테스트를 위한 테스트 코드에서Class AService { @Autowired private ARepository arepository; public void methodA() { arepository.procedure(); arepository.two(); } }위와 같이 구성이 되어 있는데 methodA에서 리포지토리의 procedure만 모킹처리를 하고싶습니다.테스트코드를 대략 아래와 같이 @Spy를 사용해서 의존성 주입을 하는데 작동이 안되어 문의드려요. 제가 한 방식이 잘못된걸까요?@ExtendWith(MockitoExtension.class) class Test { @InjectMocks; private AService aservice; @Spy private ARepository arepository; @Test void test() { doReturn("OK").when(arepository).procedure(); aservice.methodA(); } }
-
해결됨Practical Testing: 실용적인 테스트 가이드
Fixture 클렌징 관련 질문드립니다.
안녕하세요! 저는 강사님이 말씀해주신 방법들이 아닌 @Sql 어노테이션을 사용해서 모든 테이블을 Truncate하는 방법으로 클렌징을 하고 있었습니다.Truncate는 복구가 안되지만 성능 면에서 Delete보다 빠른 것으로 알고 있는데 제가 사용한 방식도 괜찮은 방법일지, 혹은 발생할만한 문제가 있을지 궁금합니다. teardown.sql-- 모든 제약 조건 비활성화 SET REFERENTIAL_INTEGRITY FALSE; truncate table user_tb; truncate table oauth_tb; truncate table vote_tb; truncate table choice_tb; truncate table vote_item_tb; truncate table place_tb; truncate table review_tb; truncate table review_vibe_tb; truncate table vibe_tb; truncate table place_vibe_tb; truncate table place_food_tb; truncate table food_tb; truncate table participant_tb; -- 모든 제약 조건 활성화 SET REFERENTIAL_INTEGRITY TRUE;
-
해결됨Practical Testing: 실용적인 테스트 가이드
반환 타입이 void인 메서드에 대한 질문이 있습니다.
안녕하세요 강의 잘 듣고 있습니다! 위 사진처럼 Service 레이어에서 반환 타입이 void인 메서드의 경우, assertJ로 검증하는 방법이 있나요? 해당 메서드에서 create 작업을 수행하지만 굳이 생성된 객체를 응답할 이유가 없어서요!예전에 임시로 반환 타입을 바꿔서 생성된 객체를 검증하는 식으로 테스트를 작성하면 된다는 얘기를 들었는데, 결국 void로 되돌리게 되면 테스트가 실패하니까 테스트를 주석 처리할 수 밖에 없었거든요...이렇게 하게 되면 테스트 코드를 작성하는 의미도 좀 퇴색되는 것 같아서 고민이 됩니다!일단은 급한대로 then은 비워두었습니다만 이렇게 하는 게 좋은 방법일까요?
-
미해결Practical Testing: 실용적인 테스트 가이드
Order에서 orderProducts 테스트하는 방법이 궁금합니다.
package sample.cafekiosk.domain.order; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.test.context.ActiveProfiles; import sample.cafekiosk.domain.product.Product; import sample.cafekiosk.domain.product.ProductRepository; import sample.cafekiosk.domain.product.ProductSellingStatus; import sample.cafekiosk.domain.product.ProductType; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.List; import static org.assertj.core.groups.Tuple.tuple; import static sample.cafekiosk.domain.product.ProductSellingStatus.*; import static sample.cafekiosk.domain.product.ProductType.HANDMADE; @ActiveProfiles("test") @DataJpaTest class OrderRepositoryTest { @Autowired private OrderRepository orderRepository; @Autowired private ProductRepository productRepository; @DisplayName("원하는 날짜와 주문 상태로 주문들을 가져온다.") @Test void findOrdersBy() { //given LocalDateTime registeredDateTime1 = LocalDateTime.of(2024, 3, 2, 12, 0); LocalDateTime registeredDateTime2 = LocalDateTime.of(2024, 3, 9, 12, 0); Product product1 = createProduct(SELLING, HANDMADE, "아메리카노", 4000, "001"); Product product2 = createProduct(HOLD, HANDMADE, "카페라떼", 4500, "002"); Product product3 = createProduct(STOP_SELLING, HANDMADE, "팥빙수", 7000, "003"); List<Product> products = List.of(product1, product2, product3); productRepository.saveAll(products); Order order1 = createOrder(registeredDateTime1, products); Order order2 = createOrder(registeredDateTime2, products); List<Order> orders = List.of(order1, order2); orderRepository.saveAll(orders); //when LocalDate findDate = LocalDate.of(2024, 3, 2); List<Order> findOrders = orderRepository.findOrdersBy(findDate.atStartOfDay(), findDate.atStartOfDay().plusDays(1), OrderStatus.PAYMENT_COMPLETED); //then Assertions.assertThat(findOrders).hasSize(1) .extracting("registeredDateTime", "orderProducts", "orderStatus") .containsExactlyInAnyOrder( tuple(registeredDateTime1, products, OrderStatus.PAYMENT_COMPLETED) ); } private Order createOrder(LocalDateTime registeredDateTime1, List<Product> products) { return Order.builder() .orderStatus(OrderStatus.PAYMENT_COMPLETED) .registeredDateTime(registeredDateTime1) .products(products) .build(); } private Product createProduct(ProductSellingStatus sellingStatus, ProductType type, String name, int price, String productNumber) { return Product.builder() .productNumber(productNumber) .type(type) .sellingStatus(sellingStatus) .name(name) .price(price) .build(); } }안녕하세요. 우빈님OrderRepository의 findOrdersBy() 테스트는 직접 작성하라고 하셔서 테스트를 작성하다가 궁금한 점이 생겨 질문 남깁니다. 첫 번째는 Order의 orderProducts를 테스트할 때, products가 프록시 객체로 넘어와서 테스트하지 못하는 거 같습니다.현재 코드는 테스트가 실패하는 코드고, extracting에서 "orderProducts"를 제외하면 성공하는 테스트가 됩니다.이럴 때는 어떻게 해결해야 할지 궁금합니다. 두 번째 질문은 제가 작성한 테스트 코드 구조에서 개선할 사항이 있는지 궁금합니다. 강의 잘 보고 있습니다.감사합니다.
-
미해결Practical Testing: 실용적인 테스트 가이드
현업에서도 TDD를 적극 활용하시는지 궁금합니다.
우빈님 강의에서 말씀해주신 것 처럼 TDD를 활용하면 테스트 코드를 먼저 작성하기 때문에 테스트에 대한 강제(?)할 수 있으며, 엣지 케이스에 대한 식별을 빨리 할 수 있어 많은 장점이 있다고 생각합니다. 제가 생각했을 때는 TDD를 활용하지 못하는 상황은 시간적 여유가 없을 때라고 생각되는데 이 외 다른 이유도 있을까요? 또한, 우빈님께선 평소 TDD를 활용하여 프로덕션 코드를 작성하는지도 궁금합니다.(개발 기간이 부족할 때도 테스트 코드를 무조건 작성하는지도 궁금합니다😀)
-
해결됨Practical Testing: 실용적인 테스트 가이드
@SQLRestriction으로 논리적 삭제 필드에 대한 tearDown에 대해 궁금합니다.
안녕하세요.강의를 들은 후 테스트 코드 작성을 연습하고 있습니다.엔티티에서 Soft delete를 위한 필드(isDeleted)가 정의되어 있습니다.Soft delete가 된 데이터는 조회할 필요가 없기 때문에 엔티티에 @SQLRestriction("is_deleted = false")를 정의했습니다.테스트 코드에서 해당 어노테이션 때문에 tearDown() 메서드 동작시 문제가 발생했습니다..deleteAllInBatch()가 실행될 때, where 조건이 포함되기 때문에 데이터가 삭제되지 않습니다. 상품 목록 조회에 대한 테스트 코드를 작성할 때, soft delete 된 데이터는 빠지고 정상적인 데이터만 조회되는지 보려고 isDeleted = true 값을 준 테스트 데이터도 생성해서 테스트 코드를 작성했습니다. 해당 경우에는 어떤식으로 테스트 코드를 작성해야 할까요? isDeleted = true 테스트 데이터를 만들지 않는다.@SQLRestriction("is_deleted = false") 를 사용하지 않고, 쿼리 조회 시 IsDeletedTrue 조건을 주도록 한다.위 방법이 아닌 Best Practice가 있는지 궁금합니다. 아래는 테스트 코드 예시 입니다.@SQLRestriction("is_deleted = false") 적용된 엔티티 예시입니다.@Entity @SQLRestriction("is_deleted = false") @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) public class Product extends BaseDateTimeEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "product_id") private Long id; @Column(length = 50) private String name; private Boolean isDeleted; @OneToMany(mappedBy = "product", cascade = CascadeType.ALL) private List<ProductOption> productOptions = new ArrayList<>(); // ... 중략 }@Entity @SQLRestriction("is_deleted = false") @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) public class ProductOption extends BaseDateTimeEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "product_option_id") private Long id; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "product_id") private Product product; @Column(length = 30) private String name; private Boolean isDeleted; // ... 중략 } isDeleted = true 테스트 데이터를 포함한 테스트 코드 예시입니다.class ProductServiceTest extends ServiceTestSupport { @Autowired private ProductRepository productRepository; @AfterEach void tearDown() { productOptionRepository.deleteAllInBatch(); productRepository.deleteAllInBatch(); } @Test @DisplayName("상품 목록을 조회한다.") void getSellingProducts() throws Exception { // given ProductOption productOption1 = createProductOption("옵션1", false); ProductOption productOption2 = createProductOption("옵션2", false); ProductOption productOption3 = createProductOption("옵션3", false); ProductOption productOption4 = createProductOption("옵션4", true); // isDeleted = true ProductOption productOption5 = createProductOption("옵션5", false); ProductOption productOption6 = createProductOption("옵션6", true); // isDeleted = true Product product1 = createProduct("상품1", false, List.of(productOption1, productOption2)); Product product2 = createProduct("상품2", false, List.of(productOption3, productOption4)); Product product3 = createProduct("상품3", true, List.of(productOption5, productOption6)); // isDeleted = true productRepository.saveAll(List.of(product1, product2, product3)); // ... 중략 // when List<product> products = productService.getSellingProducts(); // then // ... 중략 } // ... 중략 } deleteAllInBatch() 메서드가 실행된 후 로그 입니다.Hibernate: delete from product_option where ( product_option.is_deleted = false ) Hibernate: delete from product where ( product.is_deleted = false ) org.springframework.dao.DataIntegrityViolationException: JDBC exception executing SQL [delete from product where (product.is_deleted = false)] [Referential integrity constraint violation: "FKN4HMM6EX1VGN60C6UIQTE400F: PUBLIC.PRODUCT_OPTION FOREIGN KEY(PRODUCT_ID) REFERENCES PUBLIC.PRODUCT(PRODUCT_ID) (CAST(3 AS BIGINT))"; SQL statement: delete from product where (product.is_deleted = false) [23503-224]] [n/a]; SQL [n/a]; constraint ["FKN4HMM6EX1VGN60C6UIQTE400F: PUBLIC.PRODUCT_OPTION FOREIGN KEY(PRODUCT_ID) REFERENCES PUBLIC.PRODUCT(PRODUCT_ID) (CAST(3 AS BIGINT))"; SQL statement: delete from product where (product.is_deleted = false) [23503-224]] 감사합니다.
-
해결됨Practical Testing: 실용적인 테스트 가이드
강사님은 테스트를 어떻게 하시는지 궁금합니다.
학습 관련 질문을 남겨주세요. 어떤 부분이 고민인지, 무엇이 문제인지 상세히 작성하면 더 좋아요!먼저 유사한 질문이 있었는지 검색해 보세요.서로 예의를 지키며 존중하는 문화를 만들어가요. Classicist에 대해서 궁금해서 질문드립니다.현업 또는 강사님 혼자서 컨트롤러만 테스트 할 경우 서비스 레이어를 mock, stub을 사용하시는지,아니면 Fake Service 및 Fake Repository를 구현 후 컨트롤러 테스트를 하시는지 궁금합니다.Classicist을 추구하면 외부 환경만 Mock을 사용해서 테스트를 해야하는 지 아니면 최대한 Mock을 지양하지만 컨트롤러 테스트 같은 경우에는 Mock을 사용하나요?
-
미해결Practical Testing: 실용적인 테스트 가이드
OrderProduct 테스트에 대해
안녕하세요 강사님먼저 강의에 대해 너무 감사드린다는 말씀 드리고 싶습니다.강의 내용 중 OrderProduct를 생성하는 부분의 책임이 Order에 있게 설계하는 부분에 정말 매력을 느끼고, 유사한 설계 방법들에 대해 공부하려면 어떤 키워드를 알아보면 좋을까요??또한 이 강의를 따라 공부해서 개인적으로 진행중인 프로젝트에 적용하면서 공부하고 있는데, 딱 Order와 OrderProduct같은 관계에서 OrderProduct를 강의에서처럼 생성자가 아닌 정적 팩토리 메소드를 통해 만들어보고자 하였습니다.하지만 여기에서 OrderProdcut의 정적 팩토리 메소드를 테스트하려면 Order가 필요하고, Order를 create하려면 또 OrderProduct가 내부에서 만들어지는 문제가 발생했습니다.이런 경우는 어떻게 해결할 수 있을까요??임의로 Order에 대해 mock으로 생성해서 해결하고는 있는데, 이게 일반적인 방법이 될 수 있을까요?감사합니다!!
-
미해결Practical Testing: 실용적인 테스트 가이드
SpringSecurity 사용 시 Controller 테스트
안녕하세요 좋은 강의를 제공해주셔서 너무 감사합니다. 평소 테스트에 대해 고민하던 많은 부분이 해결되었습다. 그러나 아직 해결하지 못 한 부분이 있습니다.전 개인 프로젝트에 Spring Security 를 이용해 인증 및 인가를 구현하였습니다. 또한, 컨트롤러에 @AuthenticationPrincipal 을 이용해 인증 객체를 가져와 로그인한 회원의 정보를 사용하고 있습니다.강사님의 경우 @WebMvcTest 를 이용해 컨트롤러만 띄워 최소한의 파라미터만 검증하였습니다. 저 역시 처음엔 @WebMvcTest 를 이용해 최소한의 비용으로 컨트롤러를 테스트해보려 했으나 테스트 수행 시 Security 관련 빈이 없어 잦은 오류가 발생하였습니다.이 경우 @WebMvcTest 를 그대로 사용하며 TestSupport 와 같은 클래스에 시큐리티 관련 빈들을 모두 목킹하고 @AuthenticationPrincipal 으로 가져오는 인증객체 또한 목킹하는 것이 좋은 테스트일지 @SpringBootTest 를 사용한 다음 인증객체를 주입하는 것이 좋은 테스트인지 잘 판단이 안됩니다. (@AuthenticationPrincipal 의 경우 컨트롤러 메서드의 파라미터로 들어가는데 이것 역시 목킹이 가능한지도 잘 감이 안잡힙니다.)마지막으로 Spring Security 를 구성하는 필터 혹은 인터셉터나 kafka, websocket 같은 비동기 통신의 경우 실무에서 어떻게 테스트를 수행하는지 궁금합니다!긴 질문 읽어주셔서 감사합니다
-
해결됨Practical Testing: 실용적인 테스트 가이드
인수 테스트에 대한 비중은 어느정도로 가져가는게 좋다고 생각하시나요?
안녕하세요 강사님.!좋은 강의 잘 들었습니다.!덕분에 좋은 자산을 남겨 놓을 수 있을 거 같습니다. 이번 강의에서 인수 테스트에 관한 내용은 없어서 인수 테스트에 관한 강사님의 전반적인 견해가 궁금합니다. 1)다른 단위/통합 테스트에 비해서 인수 테스트에 대한 중요도가 낮다고 생각하시나요? 2)현업에서 인수 테스트에 대한 비중은 어느 정도로 가지고 가시나요? 개인적으로는 가장 사용자 친화적인(?) 인수테스트도 중요하다고 생각하고 있습니다. 감사합니다.! 좋은 하루 되세요!