묻고 답해요
141만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결Practical Testing: 실용적인 테스트 가이드
Spring Security 를 포함한 WebMvcTest 질문드립니다.
안녕하세요 좋은 강의 덕분에 테스트에 대해 많이 알게 되었습니다 :)스프링 시큐리티를 포함한 테스트와 관련해서 질문을 드려보고자 글을 쓰게 되었습니다. (강의에서는 스프링 시큐리티를 사용하지 않지만 테스트와 관련해서 물어볼 곳이 마땅치 않아 이 곳에 글을 남기는 점 양해해주시면 감사하겠습니다)@RequiredArgsConstructor @RestController @RequestMapping("/api") @Slf4j public class AuthController { private final AuthService authService; @PostMapping("/auth/sign-up") public String signUp(@RequestBody SignUpRequest request) { authService.signUp(request); return "회원가입 성공"; } }현재 회원가입을 하는 컨트롤러의 메서드는 다음과 같습니다. @Service @RequiredArgsConstructor @Slf4j @Transactional(readOnly = true) public class AuthService { private final UserRepository userRepository; private final PasswordEncoder passwordEncoder; @Transactional public void signUp(SignUpRequest request) { if (userRepository.findByEmail(request.getEmail()).isPresent()) { throw new AlreadyExistException("이미 존재하는 이메일입니다."); } if (userRepository.findByNickname(request.getNickname()).isPresent()) { throw new AlreadyExistException("이미 존재하는 닉네임입니다."); } User user = User.builder() .name(request.getName()) .email(request.getEmail()) .password(request.getPassword()) .nickname(request.getNickname()) .role(UserRole.USER) .build(); user.passwordEncode(passwordEncoder); userRepository.save(user); } }그리고 실제 회원가입이 진행되는 로직은 이렇습니다. 그리고 제가 WebMvcTest 어노테이션을 사용해서 테스트 코드를 작성한 건 다음과 같습니다.@ActiveProfiles("test") @AutoConfigureMockMvc @WebMvcTest(AuthController.class) class AuthControllerTestWithWebMvcTest { @Autowired protected MockMvc mockMvc; @Autowired protected ObjectMapper objectMapper; @MockBean private AuthService authService; @MockBean private UserRepository userRepository; @DisplayName("회원가입 한다.") @CustomMockUser // 회원가입을 해야되는데 인증이 필요하다? -> 논리적으로 말이 안 됨 -> 올바른 테스트? @Test void signUp() throws Exception { // given SignUpRequest request = SignUpRequest.builder() .name("zun") .email("zun@test.com") .password("12345") .nickname("zunny") .build(); // then mockMvc.perform(post("/api/auth/sign-up") .content(objectMapper.writeValueAsString(request)) .contentType(APPLICATION_JSON) .with(csrf()) ) .andExpect(status().isOk()) .andDo(print()); } }보시다시피 회원가입 API에 요청이 제대로 되는지를 확인하기 위해 @CustomMockUser 라는 어노테이션을 사용하고 있습니다. (이 어노테이션은 테스트에서 인증을 처리하기 위해 만든 어노테이션입니다. 호돌맨님 강의를 참고해서 만들었습니다.)제가 혼란스러운 부분은 테스트코드에 써놓은 주석처럼 회원가입이 제대로 되는지를 테스트하기 위한 컨트롤러 코드인데 이걸 통과시키기 위해 인증을 담당하는 @CustomMockUser를 사용하는 것이 올바른 테스트인가 하는 의문입니다.@WebMvcTest를 사용하지 않고 @SpringBootTest를 사용하면 다음처럼 할 수가 있습니다.@ActiveProfiles("test") @AutoConfigureMockMvc @SpringBootTest class AuthControllerTest { @Autowired private UserRepository userRepository; @Autowired private ObjectMapper objectMapper; @Autowired private MockMvc mockMvc; @BeforeEach void tearDown() { userRepository.deleteAllInBatch(); } @DisplayName("회원가입 한다.") @Test void signUp() throws Exception { //given SignUpRequest request = SignUpRequest.builder() .name("zun") .email("zun@test.com") .password("12345") .nickname("zunny") .build(); //expected mockMvc.perform(post("/api/auth/sign-up") .content(objectMapper.writeValueAsString(request)) .contentType(APPLICATION_JSON) ) .andExpect(status().isOk()) .andDo(print()); } } 혹시 이와 관련해서 우빈님은 어떻게 생각하시는지, 그리고 스프링 시큐리티를 사용하는 프로젝트에서는 컨트롤러 쪽 테스트를 어떻게 하는 것이 좋은지 알려주시면 감사하겠습니다!
-
해결됨Practical Testing: 실용적인 테스트 가이드
Stock 엔티티의 예외처리 관련하여 질문 드립니다.
안녕하세요 강사님, 먼저 좋은 강의 잘 듣고 있어서 감사의 말씀 드립니다.테스트에 대한 질문은 아니지만 강의 중 작성해주신 코드에 관하여 궁금증이 생겨 질문 드립니다.만약 문제가 되는 경우에는 삭제하도록 하겠습니다. 먼저 궁금증이 생긴 위치는 다음과 같습니다.Business Layer 테스트 (3) 강의의 31:07Stock 엔티티의 deductQuantity 메소드에서 예외를 발생시키는 코드public void deductQuantity(int quantity) { if (isQuantityLessThan(quantity)) { throw new IllegalArgumentException("차감할 재고 수량이 없습니다."); } this.quantity -= quantity; } [질문]강의 중에서 deductQuantity 메소드에서 왜 isQuantityLessThan로 검증해야 하는지 설명해 주셨는데 그 부분은 이해가 되었습니다.그런데 예외 메세지를 직접 문자열로 작성해주는 부분에서 궁금증이 생겼습니다.스프링 빈에서 MessageSource 인터페이스를 통해 별도의 properties 파일로 예외 메세지 관리가 가능한데, 엔티티는 스프링 빈으로 등록되는 것이 아니어서 MessageSource를 통해 예외 메세지를 가져올 수 없을 것이라 생각됩니다. (그러나 deductQuantity가 호출되는 시점에서 진짜로 차감할 재고 수량이 있는지 체크하는 것도 타당하다고 생각합니다.) 그렇다면 서비스에서는 MessageSource를 통해 에러 메세지를 가져오되, 엔티티에서는 문자열로 직접 작성하는 방법밖에 없을까요? (대부분의 메세지는 properties 파일에 작성해두고, 엔티티에서만 직접 문자열로 작성하는 것이 약간 통일되지 않았다는 느낌이 들기도 하는데 어쩔 수 없는 것 같기도 합니다..)혹시 현업에서도 MessageSource로 메세지 처리가 가능한 부분은 MessageSource를 사용하고, 그렇지 않는 부분에서는 직접 문자열로 작성하는 방식이 많이 사용되는지도 궁금합니다.
-
미해결Practical Testing: 실용적인 테스트 가이드
OrderProduct 클래스 질문
안녕하세요. 강사님께서 OrderProduct 클래스를 아래와 같이 작성하셨습니다.Order와 Product는 N:N 양방향 관계인데 이것을 피하기 위해 OrderProduct 클래스를 새로 생성하셨는데요.order 필드가 @ManyToOne이면 product 필드가 @OneToMany가 되어야 하는게 아닌가요?그래야 N:N 구조가 되는 것 같아서요!@Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) @Entity public class OrderProduct extends BaseEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @ManyToOne(fetch = FetchType.LAZY) private Order order; @ManyToOne(fetch = FetchType.LAZY) private Product product; public OrderProduct(Order order, Product product) { this.order = order; this.product = product; } }
-
미해결Practical Testing: 실용적인 테스트 가이드
Persistence Layer 테스트 (1) 질문
안녕하세요, 좋은 강의 잘 듣고 있습니다.강의 14분쯤에 forDisplay() 메서드를 ProductSellingType Enum 파일에서 생성을 하셨는데요.ProductService 클래스가 아닌 ProductSellingType Enum에서 생성한 이유가 있을까요?어떠한 기준으로 생성을 하셨는지 궁금합니다.추가적으로 이런 부분에 있어 특정 기준을 세우는 관련 글?을 읽고 싶은데 키워드 같은게 있을까요?
-
미해결하루만에 Cypress로 작성하는 자바스크립트 E2E 테스트 코드
Cypress 설치 과정중에서
오늘 Cypress를 교수님이 올려주신 깃허브에 올려주신 링크를 가지고 저는 Windows를 쓰는지라, 버전이나 환경 설정이 달라도 VSCode에 cypress 설치까진 성공했습니다. 근데 문제는 ./node_modules/.bin/cypress open 소스 코드가 cypress 페이지에 아무리 찾아봐도 보이지 않는겁니다. 다른 방법이 없겠습니까? 아니면 몇 년 전에 찍은 강의인지 버전이 다른건가요? ./node_modules/.bin/cypress open 링크 소스를 가지고 계신다면 저한테 답장 보낼때 같이 보내드릴수 있습니까?
-
미해결Practical Testing: 실용적인 테스트 가이드
테스트에서 @Autowired 사용하는 이유가 있나요?
찾아보니 Junit 이슈인거같은데.. 생성자 주입해도 잘 받아지는 것 같거든요
-
미해결Practical Testing: 실용적인 테스트 가이드
브라우저에 json 예쁘게 출력하는거 어떤 확장인가요?
궁금합니당
-
미해결따라하며 배우는 리액트 테스트 [2023.11 업데이트]
스타일 컴포넌트 테스트 방법
강의에선 일반 html/css를 사용하고 data-testid를 사용해 엘리먼트를 선택하는데styled-component를 사용한다면 어떤 방식으로 선택하고 테스트하는게 권장되는 방법일까요?
-
미해결스프링부트 JUnit 테스트 - 시큐리티를 활용한 Bank 애플리케이션
완강 하였습니다. CI CD는
https://github.com/codingspecialist/JUNIT5-Security-Lecture 여기 나와있는 deploy.yml은 그대로 써도 되는걸까요?
-
미해결따라하며 배우는 리액트 테스트 [2023.11 업데이트]
msw 에러
강의를 보다 msw부분에서 에러가 발생합니다. 폴더구조는 이런식이고 handler와 server의 ts파일입니다.setupTests.ts파일입니다 App컴포넌트 입니다 문제의 테스트 코드입니다.강의에서 사용하신 대로 비슷하게 작성하고 에러가 발생할 부분도 제눈에는 보이진 않는데위와같은 에러가 발생하여 옵셔널 체이닝 을 사용하면이런 에러가 발생하네요.npm test가아닌 npm start로 실행시켜 봤을때에는 전혀 이상이 없는데 테스트를 실행시키면 에러가 발생합니다.서버설정부분이나 핸들러부분이 잘못돼었나 싶어서 찾아보았지만 그부분은 이상이 없는것같고app컴포넌트를 다른 방식으로 작성해도 계속에러가 발생합니다.어떤부분이 잘못된부분이고 어떤 해결방안이있을까요?
-
미해결Java/Spring 주니어 개발자를 위한 오답노트
fake Repository로 테스트를 진행하려고 합니다,
실제 유저 엔티티 가지고 userRepository를 해야하나요? 이것을 가지고 테스트를 해봤는데 id를 자동 매핑으로 해놔서 실제 fake로 테스트할때 id 값을 넣어줄 수가 없어서 구현에 어려움을 겪고 있습니다. 새로 Fake에 적용될 유저 도메인을 작성해야하나요?
-
미해결따라하며 배우는 리액트 A-Z[19버전 반영]
리액트가 계속 로딩만되고 창이안떠요
강의 다잘따라왔는데 ㅠㅠ 왜이러죠 ? 컴퓨터를껏다켜도이래요 파일에 에러는 없습니다
-
미해결스프링부트 JUnit 테스트 - 시큐리티를 활용한 Bank 애플리케이션
Fetch join 테스트에서
join을 사용하면 주체인 Transaction만 select 되고 account가 나중에 따로 조회되는 말도 안되는 쿼리도 n+1의 경우인걸까요??
-
미해결따라하며 배우는 리액트 A-Z[19버전 반영]
Banner.css가 안보이네요!
css가 전체적으로 안보여줘서 작성을 못했네요ㅜㅜ 파일 부탁드립니다.
-
미해결Practical Testing: 실용적인 테스트 가이드
테스트 동시성 관련 질문드립니다
안녕하세요 강의 다 듣고 기능 추가해 가며 공부를 하고 있습니다. 그러다 막히는 부분이 있어 질문드리는데 강의 내용을 조금 벗어 나는거 같아 질문을 드려도 될지 모르겠는데 괜찮으시다면 답변 주시면 감사하겠습니다. @Transactional public OrderResponse createOrder(OrderCreateServiceRequest request, LocalDateTime registeredDateTime) { List<String> productCodes = request.getProductCodes(); List<Product> products = findProductsBy(productCodes); Member member = memberRepository.findByPhoneNumber(request.getPhoneNumber()).get(); deductStockQuantities(products); Order order = Order.create(products, member, registeredDateTime); return OrderResponse.of(orderRepository.save(order)); }order를 생성하는 부분에서 재고 감소 되는 부분을 동시성 처리를 해보려 하는데 테스트 코드에선 deductStockQuantities로 넘어가서 findAllByProductCodeIn 만 한번 돌고 롤백이 되더라구요. @Test public void create_order_with_concurrent_5_request() throws InterruptedException { //given createProducts(); OrderCreateServiceRequest request1 = OrderCreateServiceRequest.builder() .productCodes(List.of("A002","A003")) .phoneNumber("010-1111-2222") .build(); OrderCreateServiceRequest request2 = OrderCreateServiceRequest.builder() .productCodes(List.of("A002","A003")) .phoneNumber("010-1111-2222") .build(); OrderCreateServiceRequest request3 = OrderCreateServiceRequest.builder() .productCodes(List.of("A002","A003")) .phoneNumber("010-1111-2222") .build(); int numberOfThreads = 3; ExecutorService executorService = Executors.newFixedThreadPool(numberOfThreads); CountDownLatch latch = new CountDownLatch(numberOfThreads); //when executorService.submit(() -> { try { orderService.createOrder(request1, LocalDateTime.now()); }finally { latch.countDown(); } }); executorService.submit(() -> { try { orderService.createOrder(request2, LocalDateTime.now()); }finally { latch.countDown(); } }); executorService.submit(() -> { try { orderService.createOrder(request3, LocalDateTime.now()); }finally { latch.countDown(); } }); latch.await(); //then List<Stock> stocks = stockRepository.findAllByProductCodeIn(List.of("A002","A003")); assertThat(stocks).hasSize(2) .extracting("productCode", "quantity") .containsExactlyInAnyOrder( tuple("A002", 7), tuple("A003", 7) ); } @Lock(LockModeType.PESSIMISTIC_WRITE) List<Stock> findAllByProductCodeIn(List<String> productCodes);테스트 코드는 구글링해서 넣어보았는데 이런 부분 관련해서 따로 배운게 없어 잘 안되더라구요. 락도 걸어보고 했는데 어디서 안되는지 잘 모르겠어서 질문드립니다.
-
미해결Practical Testing: 실용적인 테스트 가이드
이 경우, @BeforeEach에 fixture를 구성해도 될까요?
지금 OrderServiceTest 클래스의 createOrder(), createOrderWithStock(), ... 메서드들은 주문을 생성하기 위해서 given절에서 모두 동일하게 product1, 2, 3을 먼저 생성해주는 fixture가 있습니다. @Test void createOrder() { // given LocalDateTime registeredDateTime = LocalDateTime.now(); Product product1 = createProduct(HANDMADE, "001", 1000); Product product2 = createProduct(HANDMADE, "002", 3000); Product product3 = createProduct(HANDMADE, "003", 5000); productRepository.saveAll(List.of(product1, product2, product3)); OrderCreateServiceRequest request = OrderCreateServiceRequest.builder() .productNumbers(List.of("001", "002")) .build(); // when OrderResponse orderResponse = orderService.createOrder(request, registeredDateTime); // then 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) ); }product를 생성해주는 부분을 @BeforeEach로 빼내려고 하는데, 적절하다고 판단되시는지 궁금합니다. OrderServiceTest의 모든 메서드가 동일한 product를 생성하고는 있지만,각 테스트 메서드에서 "001"이라는 상품이 있는지는 @BeforeEach 메서드까지 스크롤을 왔다갔다하면서 확인해야될거라 생각합니다.(만약 없는 상품을 주문한다면 given절에서 예외가 발생하는 것이기 때문에, 이 부분은 당연히 있는 상품을 주문한다는 것을 보장하고 테스트 코드를 작성해야 할까요?) order에 관한 테스트이기 때문에, product에 대한 부분은 분리해도 될거라 생각하지만, 우빈님의 말씀대로 스크롤을 왔다갔다하면서 @BeforeEach의 메서드를 수시로 보는 것은 안좋다고 느껴져서.. product 생성에 대한 fixture를 분리괜찮다고 생각하시나요?
-
미해결Java/Spring 테스트를 추가하고 싶은 개발자들의 오답노트
jpa의 더티체킹 사용에 대해서
해당 강의에서 도메인 모델과 영속성 객체를 구분하는 리팩토링을 진행하셨는데요.리팩토링 이후 영속성 컨텍스트를 통한 더티체킹을 활용하지 않고, 대신 save 메서드를 매번 호출해 주시는 방법으로 변경하셨더라구요.도메인 모델과 영속성 객체의 의존성을 제거하기 위해서 더티체킹 기능도 사용하지 않는건가요?
-
미해결Practical Testing: 실용적인 테스트 가이드
섹션8 - Spring REST Docs
학습 관련 질문을 남겨주세요. 어떤 부분이 고민인지, 무엇이 문제인지 상세히 작성하면 더 좋아요!먼저 유사한 질문이 있었는지 검색해 보세요.서로 예의를 지키며 존중하는 문화를 만들어가요.안녕하세요. 강의를 100% 다 소화하지는 못했지만 일단 거진 한바퀴는 돌리면서 마지막에 문제가 생겨 질문 드립니다.plugins { id 'java' id 'org.springframework.boot' version '2.7.7' id 'io.spring.dependency-management' version '1.0.15.RELEASE' id "org.asciidoctor.jvm.convert" version "3.3.2" } group = 'sample' version = '0.0.1-SNAPSHOT' java { sourceCompatibility = '11' } configurations { compileOnly { extendsFrom annotationProcessor } asciidoctorExt } repositories { mavenCentral() } dependencies { // Spring boot implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-validation' // test testImplementation 'org.springframework.boot:spring-boot-starter-test' // lombok compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' //테스트에서 lombok 사용 testCompileOnly 'org.projectlombok:lombok' testAnnotationProcessor 'org.projectlombok:lombok' // h2 runtimeOnly 'com.h2database:h2' // Guava implementation("com.google.guava:guava:32.1.1-jre") // RestDocs asciidoctorExt 'org.springframework.restdocs:spring-restdocs-asciidoctor' testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc' } tasks.named('test') { useJUnitPlatform() } ext { // 전역 변수 snippetsDir = file('build/generated-snippets') } test { outputs.dir snippetsDir } asciidoctor { inputs.dir snippetsDir configurations 'asciidoctorExt' sources { include("**/index.adoc") } baseDirFollowsSourceDir() dependsOn test } bootJar { dependsOn asciidoctor from ("${asciidoctor.outputDir}") { into 'static/docs' } } 일단 build.gradle 내용이구요. 운영체제는 윈도우 입니다.마지막에 build 후 jar파일을 실행하여 localhost:8080/docs/index.html 을 확인해보려고 하니 접속이 되지 않아 확인해보니 resources - static 디렉토리에 index.html이 생성되지 않았음을 확인하였습니다. build 디렉토리에는 생성되어 있었구요.몇번이나 clean 후 build를 통해 build쪽에는 index.html이 생성 되는 것을 확인 하였으나, static 패키지 하위에 docs 패키지 조차 생성되지 않아 제가 뭔가 오타를 낸건지 운영체제 문제인지 질문 드리고자 합니다. 감사합니다.
-
미해결Java/Spring 테스트를 추가하고 싶은 개발자들의 오답노트
완강 후 질문이 있습니다~!
안녕하세요 재밌고 알찬 강의 제공해 주셔서 감사합니다.jpa 와 같이 findById 같은 기본적으로 제공해주는 메서드는 따로 테스트를 안해봐도 될거 같은데..queryDsl 식으로 커스텀한 쿼리를 호출할때 테스트 방법은 어떻게 해야할지 궁금합니다.. h2 와 같은 임베디드디비로 쿼리 조건과 데이터가 잘 나오는지 테스트도 짜고, 다른 인터페이스를 상속받은 구현체의 메서드에서 메모리를 사용해 stream 과 filter 식으로 데이터를 반환하는 구현체를 테스트로 다시 넣어야하는지.. h2로 테스트코드를 짜고, 확인 후, 해당 테스트 코드는 지워버려야 하는지.. 강사님은 어떻게 하고 있는지 궁금합니다.. 감사합니다.
-
해결됨Java/Spring 주니어 개발자를 위한 오답노트
DB 엔티티와 도메인 분리
어디까지 추상화 해야 하는가? 강의에서 2분 17초에toDomain()으로 바꿔서 반환을 했는데이렇게하면 JPA 엔티티가 아니라서 변경감지를 사용하지 못하는거 아닌가요? 이러한 방식으로 작성한다면 업데이트가 필요할 때는 어떤식으로 처리하나요?