24%
65,180원
다른 수강생들이 자주 물어보는 질문이 궁금하신가요?
- 미해결호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)
테스트 코드 작성시
회원가입 기능을 구현할 때 컨트롤러에는 다음과 같은 코드가@PostMapping("/signup") public void signup(@RequestBody @Valid MemberSignup memberSignup) { memberService.signup(memberSignup); }서비스에는 다음과 같은 코드가@Transactional public void signup(MemberSignup memberSignup) { if (validateDuplication(memberSignup)) { throw new NameDuplicateException(); } Member member = new Member(memberSignup); memberRepository.save(member); }있다고 할 때 컨트롤러 테스트에서는 @Valid로 인한 실패와 회원 가입 성공 정도만 보여주고 중복 검증은 서비스 테스트에서 하면 되나요?아니면 컨트롤러에서도 모두 테스트하는 것이 좋은 방법인가요?단위 테스트를 만드는 것이 좋다고 들었는데 테스트 코드가 너무 무거워 지는 것 같아서 질문드립니다.감사합니다!
- 미해결호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)
뷰 배포도 강의계획 있으신가용??
뷰 배포도 강의계획 있으신가용??
- 해결됨호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)
키보드 소리가 너무좋은데..
강의외적 질문이지만.. 혹시 어떤 키보드를 쓰고계신지 알수있을까요..? 소리가 너무 좋아서 악기같아요
- 미해결호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)
테스트 코드 작성시 오류
밑에 글 해결했습니다! 수정하려다가 삭제해버려서..@RequestMapping(/admin/api)되어 있는걸 못봤습니다..ㅎ
- 미해결호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)
테스트 코드 작성시 오류
강의 보면서 따로 프로젝트를 진행하고 있는데 테스트 코드 작성시 오류가 납니다.. 오류가 나는 부분은 test- item -controller- ItemControllerAdminTest 의 상품 수정 테스트updatePants 메소드입니다. itemRepository에 값도 잘 저장되고 id값도 일치하는데 mockMvc patch로 테스트해보면 200이 아니라 404에러가 나옵니다.. 혹시 몰라서 main뿐만 아니라 test에도 @transaction을 달아줬는데도 에러가 발생하네요...도와줘요.. 호돌맨..!깃헙 주소입니다.https://github.com/320Hwany/ShoppingMall감사합니다~
- 미해결호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)
QClass가 생성되지만 import가 안되는 문제에 관하여
plugins { id 'java' id 'org.springframework.boot' version '2.7.7' id 'io.spring.dependency-management' version '1.0.15.RELEASE' } group = 'com.tony' version = '0.0.1-SNAPSHOT' sourceCompatibility = '11' configurations { compileOnly { extendsFrom annotationProcessor } } repositories { mavenCentral() } dependencies { 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' implementation 'com.querydsl:querydsl-core' implementation 'com.querydsl:querydsl-jpa' annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jpa" annotationProcessor 'jakarta.persistence:jakarta.persistence-api' annotationProcessor 'jakarta.annotation:jakarta.annotation-api' compileOnly 'org.projectlombok:lombok' runtimeOnly 'com.h2database:h2' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' }안녕하세요 호돌맨 선생님 질문이 있습니다.위와 같은 build.gradle 로 했을시에 build/generated에 QPost.java 가 생성되는것을 확인했지만실제 PostRepositoryImpl에서 import를 해올수 없었습니다.구글링을 하며 여러 시도를 했었지만 다 실패해더라구요하지만 운좋게 아래와 같은 설정( https://velog.io/@soyeon207/QueryDSL-Spring-Boot-%EC%97%90%EC%84%9C-QueryDSL-JPA-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0 )을 찾아서 import하는데 성공했습니다.plugins { id 'java' id 'org.springframework.boot' version '2.7.7' id 'io.spring.dependency-management' version '1.0.15.RELEASE' } group = 'com.tony' version = '0.0.1-SNAPSHOT' sourceCompatibility = '11' configurations { compileOnly { extendsFrom annotationProcessor } } repositories { mavenCentral() } dependencies { 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' implementation 'com.querydsl:querydsl-core' implementation 'com.querydsl:querydsl-jpa' annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jpa" annotationProcessor 'jakarta.persistence:jakarta.persistence-api' annotationProcessor 'jakarta.annotation:jakarta.annotation-api' compileOnly 'org.projectlombok:lombok' runtimeOnly 'com.h2database:h2' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' } def querydslSrcDir = 'src/main/generated' sourceSets { main { java { srcDirs += [ querydslSrcDir ] } } } compileJava { options.compilerArgs << '-Aquerydsl.generatedAnnotationClass=javax.annotation.Generated' } tasks.withType(JavaCompile) { options.generatedSourceOutputDirectory = file(querydslSrcDir) } clean { delete file(querydslSrcDir) } tasks.named('test') { useJUnitPlatform() } 이 방식으로 하면 build/generated에는 프로젝트 폴더들만 생기고 src/main/generated에 Post.java 가 생겨서 PostRepostioryImpl에도 잘 import가 되는데 이렇게 해도 추후에 문제가 되는부분이 있을까요?
- 미해결호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)
테스트 전체 실행시 id 값이 4부터 시작하는 현상
@SpringBootTest @AutoConfigureMockMvc @PropertySource("classpath:messages.properties") class PostControllerTest { @Autowired private ObjectMapper mapper; @Autowired private Environment environment; @Autowired private MockMvc mockMvc; @Autowired private PostRepository postRepository; @BeforeEach void clean() { postRepository.deleteAll(); } @Test @DisplayName("/posts 요청시 Hello World를 출력한다.") void test() throws Exception { // given PostCreate request = PostCreate.builder() .title("제목입니다.") .content("내용입니다.") .build(); String json = mapper.writeValueAsString(request); // expected mockMvc.perform(MockMvcRequestBuilders.post("/posts") .contentType(MediaType.APPLICATION_JSON) .content(json) // .content("{\"title\": \"hithere\", \"content\": \"blah\"}") ) .andExpect(MockMvcResultMatchers.status().isOk()) .andExpect(MockMvcResultMatchers.content().string("")) .andDo(print()); } @Test @DisplayName("/posts 요청시 title 값은 필수다.") void test2() throws Exception { Class<? extends PostControllerTest> aClass = getClass(); ClassLoader classLoader = getClass().getClassLoader(); URL resource = classLoader.getResource("messages.properties"); // given PostCreate request = PostCreate.builder() .content("내용입니다.") .build(); String json = mapper.writeValueAsString(request); // expected mockMvc.perform(MockMvcRequestBuilders.post("/posts") .contentType(MediaType.APPLICATION_JSON) .content(json) ) .andExpect(MockMvcResultMatchers.status().isBadRequest()) .andExpect(MockMvcResultMatchers.jsonPath("$.code").value("400")) .andExpect(MockMvcResultMatchers.jsonPath("$.message").value("잘못된 요청입니다.")) .andExpect(MockMvcResultMatchers.jsonPath("$.validation.title").value(environment.getProperty("post.NotBlank"))) .andDo(print()); } @Test @DisplayName("/posts 요청시 db에 값이 저장된다.") void test3() throws Exception { // given PostCreate request = PostCreate.builder() .title("제목입니다.") .content("내용입니다.") .build(); String json = mapper.writeValueAsString(request); // expected mockMvc.perform(MockMvcRequestBuilders.post("/posts") .contentType(MediaType.APPLICATION_JSON) .content(json) ) .andExpect(MockMvcResultMatchers.status().isOk()) .andDo(print()); // then assertEquals(1L, postRepository.count()); Post post = postRepository.findAll().get(0); assertEquals("제목입니다.",post.getTitle()); assertEquals("내용입니다.",post.getContent()); } @Test @DisplayName("글 1개 조회") void test4() throws Exception { // given Post post = Post.builder() .title("123456789012345") .content("bar") .build(); postRepository.save(post); // when // expected mockMvc.perform(MockMvcRequestBuilders.get("/posts/{postId}",post.getId()) .contentType(MediaType.APPLICATION_JSON)) .andExpect(MockMvcResultMatchers.status().isOk()) .andExpect(MockMvcResultMatchers.jsonPath("$.id").value(post.getId())) .andExpect(MockMvcResultMatchers.jsonPath("$.title").value("1234567890")) .andExpect(MockMvcResultMatchers.jsonPath("$.content").value("bar")) .andDo(print()); // then } @Test @DisplayName("글 여러개 조회") void test5() throws Exception { // given List<Post> requestPosts = IntStream.range(1,31) .mapToObj(i -> Post.builder() .title("title " + i) .content("content " + i) .build() ).collect(Collectors.toList()); postRepository.saveAll(requestPosts); // expected mockMvc.perform(MockMvcRequestBuilders.get("/posts?page=1&sort=id,desc") .contentType(MediaType.APPLICATION_JSON)) .andExpect(MockMvcResultMatchers.status().isOk()) .andExpect(MockMvcResultMatchers.jsonPath("$.length()",Matchers.is(5))) .andExpect(MockMvcResultMatchers.jsonPath("$[0].id").value(30)) .andExpect(MockMvcResultMatchers.jsonPath("$[0].title").value("title 30")) .andExpect(MockMvcResultMatchers.jsonPath("$[0].content").value("content 30")) .andDo(print()); } }안녕하세요 호돌맨님 질문이 있습니다.test5를 단독으로 실행하면 문제없는데전체를 실행했을 때는 postRepository.saveAll 전까지는 requestPosts 안에 Post가 id가 1부터 시작하는데saveAll을 지나고 나서는 id가 4부터 시작하게됩니다.@BeforeEach에서 postRepository.deleteAll을 수행하고 있습니다. 제생각에는 이게 Entity 의 @GeneratedValue(strategy = GenerationType.IDENTITY) 와 연관이 있는건가요?해당 설정을 하면 말씀해주신것처럼 persist가 호출되는 시점에 id값을 db로부터 얻어오기 때문에 아무리 row가 지워졌어도 h2 내부적으로 id를1씩 증가시켜왔기에 그 증가된 값이 requestPosts 의 값도 변형시킨것이 맞나요?
- 미해결호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)
vue 관련.. localhost error?
당연히 구글링 해보셨져? 원하는 결과를 못찾으셨나요? 어떤 검색어를 입력했는지 알려주세문제가 발생한 코드(프로젝트)를 Github에 올리시고 링크를 알려주세요.//vite.config.ts import { fileURLToPath, URL } from "node:url"; import { defineConfig } from "vite"; import vue from "@vitejs/plugin-vue"; import vueJsx from "@vitejs/plugin-vue-jsx"; // https://vitejs.dev/config/ export default defineConfig({ plugins: [vue(), vueJsx()], resolve: { alias: { "@": fileURLToPath(new URL("./src", import.meta.url)), }, }, server: { proxy: { "/api": { target: "http://localhost:8080", rewrite: (path) => path.replace(/^\/api/, ""), }, }, }, }); <!--WriteView.vue--> <script setup lang="ts"> import {ref} from "vue"; import axios from 'axios'; const title = ref("") const content = ref("") const write = function() { axios.post("/api/posts",{ title: title.value, content: content.value, }); }; </script> <template> <div> <el-input v-model="title" placeholder="제목을 입력해주세요"/> </div> <div> <div class="mt-2"> <el-input v-model="content" type = "textarea" rows="15"/> </div> </div> <div class="mt-2"> <el-button type="primary" @click="write()">등록</el-button> </div> </template> <style> </style> 등록 버튼 누를시, 위 사진과 같은 에러가 발생합니다.TCPConnectWrap.afterConnect 검색을 통해서 5173 포트를 UDP/TCP 포트를 다 열어봤으나 달라지는건 없었습니다.일단 진행중인 현재 실행 환경은windows에 wsl2를 설치해서 진행중이고,node: v18.12.1npm: 8.19.2vite.config.ts파일의target: "http://localhost:8080",아래와 같이 5173으로 바꾸었더니 404 NotFound 에러가 발생합니다.target: "http://localhost:5173",네트워크 관련 지식이 모자라서 하는 질문인것 같기도 합니다.뭔가 질문이 모자라 보이는것 같기도 합니다. 더 필요한 내용이 있으면 알려주세요!
- 미해결호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)
mock postService#write
안녕하세요 호돌맨님 강의 듣던 도중 mock 으로도 할 수 있다해서 고민해보는중 잘 모르겠어서 질문을 올립니다.public void write(PostCreate postCreate) { Post post = Post.builder() .title(postCreate.getTitle()) .content(postCreate.getContent()) .build(); postRepository.save(post); }위와 같은 write 메소드를 검증하려면 test 코드에서posRepostiroy를 mock으로 주입받고 postService.write(postCreate)를 호출시 postRepository.save를 호출할때 어떤 일을 하게 될것이냐 를 stubbing 하려고 하는데 실제 h2 메모리 db에 저장을 해야, 제대로 저장이 되었다는 것을 아래 처럼 검증할 수 있을 것 같은데 Assertions.assertEquals(1L, postRepository.count());위 write 메소드의 경우 아무것도 return 하지 않으니 save 메소드 stubbing을 어찌 해야할지 모르겠습니다... 아니면 접근 자체가 틀렸을까요? 실제 외부와 연동이 잘되는지 확인하는것이니 mock 쓰겠다는 생각 자체가 에러인것인가요? 감사합니다.
- 미해결호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)
배포할때 질문이 있는데요
호돌맨님이 만드신거 잘따라서 만들었는데 제가 JPA로 혼자 만들어서 따라서 배포하려고 해봤는데 다 정상적이게 됐는데ec2에서 jar파일 실행시키면 db가 연결이 안되어있으니까entityManagerFactory생성이 안되는것 같더라구요 그래서 에러나는데 저는 mysql서버를 안띄워서 그런거 같은데 근데 호돌맨님이 만드신건 어떻게 db없이 띄운거예요?h2db라 내장메모리여서 가능한건가요?
- 미해결호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)
vue3.js 배포는 혹시 예정에 있을까요??
혹시 vue3.js 배포 강의도 예정되어 있나요????
- 미해결호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)
DB 테이블을 직접 생성하지 않는것 같은데???
안녕하세요!2가지 질문이 있습니다.아직 초반부이긴 하지만,테이블을 직접 생성하시는게 없는것 같은데프로젝트 옵션으로 H2를 선택한것은 봤지만,테이블이 자동으로 생성되게 하는 옵션 같은걸 세팅 하시는것은 보지 못한것 같아서요뭔가 자동 생성되는 옵션이 있는 건가요??아니면 다른 무언가가 있는지 궁금하네요저는 보면서 mariadb 로 설정해서 하고 있는데 저는 테이블을 직접 생성하면서 진행을 해봐야 하는건지.. 2번째 질문은 test에서 before을 통해 DELETE all을 하시는 것을 봤는데, 이건 로컬 DB이기때문에 편하게 하신건지..물론 이 강의는 개인 프로젝트지만, 만약 팀원과 공유하여 사용하는 DB라고 가정하면,이렇게 하면 안될 듯 해서요. 그건 제가 알아서 방안을 생각하면 되는 건지
- 미해결호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)
Entity 상속관계 매핑 설정 시 request , edit , response 클래스 분리
안녕하세요! 호돌맨님 해당 강의를 보면서 토이프로젝트를 진행 중 질문이 생겨서 글을 씁니다.간단한 Quiz 맞추는 프로젝트를 만들고 있는데 Quiz 엔티티에 JPA 싱글테이블 전략 상속관계로 주관식 , 객관식 , 서술형으로 자식엔티티를 생성했습니다.자식엔티티를 생성 하고 보니 기존 Quiz의 Request , Edit , Response 클래스를 어떻게 변경해야하는지 답이 안 생겨서 이렇게 질문 드립니다. 🙇🏻♂️🙇🏻♂️🙇🏻♂️🙇🏻♂️자식 엔티티 각각 Request , Edit , Response 클래스 생성 -> 컨트롤러 파라미터에서 어떻게 받지? 컨트롤러 메서드로 분리시켜야하나? -> 그럼 프론트엔드에서는 더더욱 복잡해진다.기존 Quiz Request, Edit , Response 의 각각 자식 엔티티의 필드 추가 -> 컨트롤러 파라미터에거 받을 수 있고, 컨트롤러 메서드가 분리 필요 X -> 컨트롤러에서 데이터 검증은 어떻게 진행되는거지? 객관식인 경우 보기 필드가 필수값일텐데 보기 멤버 변수에 @NotBlank를 추가한다면 주관식이나 서술형 문제유형일 경우엔 오류가 나타나는 상황코드가 추가된 노션 링크 전달 드리겠습니다.https://devysk.notion.site/Entity-request-edit-response-c7433eccd6e24ee486a60bd59013a2a8
- 미해결호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)
@ControllerAdvice 없이 예외처리 하는 방법은 어떻게 되나요?
안녕하세요 호돌맨님 강의 잘 듣고 있습니다!강의를 들으면서 저도 비슷한 방법으로 게시판을 만들어 보려고 하는데요. 예외처리 부분에서 문득 궁금증이 생겨서 질문을 드립니다.혹시 @ControllerAdvice 없이 예외처리하는 방법은 뭐가 있을까요?Controller@GetMapping("/{postId}") public ResponseEntity<ResponsePostDto> getPost(@PathVariable Long postId) { ResponsePostDto responsePostDto = postService.get(postId); return ResponseEntity.ok(responsePostDto); }Servicepublic ResponsePostDto get(Long postId) { Post post = postRepository.findById(postId) .orElseThrow(() -> new RuntimeException("존재하지 않는 게시글입니다.")); return ResponsePostDto.builder() .id(post.getPostId()) .title(post.getTitle()) .content(post.getContent()) .createdDate(post.getCreatedDate()) .build(); }컨트롤러와 서비스를 이렇게 작성하고 @ControllerAdvice 없이 테스트를 돌려보니 서비스 계층에서의 예외처리 테스트 코드는 RumtimeException을 받으면서 잘 돌아갑니다.그리고 컨트롤러 테스트 코드를 작성했습니다.ControllerTest@Test @DisplayName("게시글 조회 실패 - 잘못된 ID") void getPostBadIdTest() throws Exception { // given ResponseSavedIdDto responseSavedIdDto = postService.write(RequestRegisterPostDto.builder() .title("test title") .content("test content") .build()); Long postId = responseSavedIdDto.getSavedId(); // expected mockMvc.perform(get("/posts/{postId}", postId + 1L) .contentType(APPLICATION_JSON)) .andExpect((result) -> { System.out.println("==================="); System.out.println("message: " + result.getResolvedException().getMessage()); System.out.println("==================="); Assertions.assertEquals(result.getResolvedException().getClass().getCanonicalName(), RuntimeException.class.getCanonicalName()); Assertions.assertTrue(result.getResolvedException().getClass().isAssignableFrom(RuntimeException.class)); }) .andDo(print()); }이렇게 컨트롤러 테스트를 돌려보니 서비스에서 터진 RuntimeException이 컨트롤러까지 전달이 안되는것으로 확인했습니다. 그리고 나서 컨트롤러에서 try catch로 RuntimeException을 잡는 방식으로 수정해봤습니다.@GetMapping("/{postId}") public ResponseEntity<ResponsePostDto> getPost(@PathVariable Long postId) { try { ResponsePostDto responsePostDto = postService.get(postId); return ResponseEntity.ok(responsePostDto); } catch (Exception e) { throw new RuntimeException(e); } }이렇게 하니 RumtimeException은 컨트롤러에서 터지긴 하는데 테스트 코드에서는 andExpect에서 Exception 결과를 받지 못하는지 통과가 계속 안됩니다..ㅠㅠ강의에서 나온대로 @ControllerAdvice와 @ExceptionHandler를 사용하면 통과가 되는데 컨트롤러에서 try catch로 Exception을 던지는것과 어떤 차이가 있길래 테스트 코드에서 차이점이 생기는 걸까요? 그리고 @ControllerAdvice를 사용하지 않는다면, 서비스 계층에서 생긴 Exception을 어떻게 컨트롤러에서 처리하면서 테스트 코드를 통과할 수 있을까요??답변 주시면 감사하겠습니다!!
- 미해결호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)
Spring Security vs ArugmentResovler
안녕하세요. 전에는 Spring Security 로 인증처리를 했는데 ArugmentResovler로와 큰 차이점이 있을까요? 구글링을 해봐도 정확한 차이를 잘 모르겠네요. 간단하게 쓸 때는 ArugmentResovler를 사용하는 건가요?
- 미해결호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)
안녕하세요 호돌맨님 영상 잘봤습니다!
우선 영상을 너무 잘봤습니다. 저에게 도움이 많이 되었어요! 영상을 보고 만든 소스들을고도화하여 블로그를 만드려고합니다.H2를 사용하셨는데만약 MySql로 DB를 바꾼다면 어떤식으로 진행해야하는지 힌트?라도 주실수있을까요?
- 미해결호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)
실습 예제 소스 공유 신청합니다.
안녕하세요.참고용 소스 git 신청하라는 공지 봤을때는 결제만 하구 아직 수업듣기 전이라..들을때 신청해야지 했었는데요.지금 들을려구 보니 새소식에서 목록이 사라져 있어서요.혹시 git 읽기 권한 가능하다면 공유 부탁드립니다.인프런 id : startek7@daum.netgithub 로그인 메일 : startek7@daum.net감사합니다.
- 미해결호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)
인텔리J 플러그인이 궁금합니다
강의 중간 중간에 호돌맨님께서 빌드 후 테스트 하고나면 캡쳐화면처럼 방금 전에 수행한 후 line by line 으로 각 변수의 사이즈, 값, 타입들이 나오더라고요.신기한 플러그인 같은데 혹시 어떤 플러그인일까요??감사합니다.
- 미해결호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)
postService 목록 조회 단위 테스트
당연히 구글링 해보셨져? 원하는 결과를 못찾으셨나요? 어떤 검색어를 입력했는지 알려주세문제가 발생한 코드(프로젝트)를 Github에 올리시고 링크를 알려주세요. 안녕하세요. 호돌맨님.postService에서 목록 조회하는 단위테스트에 대해서 질문이 있습니다. 먼저 목록 조회 코드부터 보여드리면postService.findPosts 는 postQueryRepository에 구현체로 만들었습니다.postService@Slf4j @Service @RequiredArgsConstructor @Transactional(readOnly = true) public class PostService { public static final String ENTITY_NAME = "post"; private final PostQueryRepository postQueryRepository; private final PostRepository postRepository; /** * Post 목록 조회 */ public Page<Post> findPosts(PostSearchCondition condition, Pageable pageable) { return postQueryRepository.findPosts(condition, pageable); } }postQueryRepository@Repository public class PostQueryRepository { private final JPAQueryFactory queryFactory; public PostQueryRepository(EntityManager em) { this.queryFactory = new JPAQueryFactory(em); } /** * Post 목록 조회 */ public Page<Post> findPosts(PostSearchCondition condition, Pageable pageable) { List<Post> content = getPostList(condition, pageable); JPAQuery<Long> count = getPostListCount(condition); return PageableExecutionUtils.getPage(content, pageable, count::fetchOne); } /** * Post 목록 */ private List<Post> getPostList(PostSearchCondition condition, Pageable pageable) { return queryFactory .select(post) .from(post) .where( searchCondition(condition.getSearchCondition(), condition.getSearchKeyword()) ) .offset(pageable.getOffset()) .limit(pageable.getPageSize()) .orderBy(post.id.desc()) .fetch(); } /** * Post 목록 카운트 */ private JPAQuery<Long> getPostListCount(PostSearchCondition condition) { return queryFactory .select(post.count()) .from(post) .where( searchCondition(condition.getSearchCondition(), condition.getSearchKeyword()) ); } /** * where searchCondition LIKE '%searchKeyword%' */ private BooleanExpression searchCondition(SearchCondition searchCondition, String searchKeyword) { if (searchCondition == null || !hasText(searchKeyword)) { return null; } if (SearchCondition.TITLE.equals(searchCondition)) { return post.title.contains(searchKeyword); } else if (SearchCondition.CONTENT.equals(searchCondition)) { return post.content.contains(searchKeyword); } else { return null; } } } 다음은 테스트 코드입니다.postServiceTest@ExtendWith(MockitoExtension.class) class PostServiceTest { //CREATE_POST public static final String POST_TITLE = "post_title"; public static final String POST_CONTENT = "post_content"; //UPDATE_POST public static final String UPDATE_TITLE = "update_title"; public static final String UPDATE_CONTENT = "update_content"; //ERROR_MESSAGE public static final String ENTITY_NAME = "post"; public static final Long NOT_FOUND_ID = 1L; public static final String HAS_MESSAGE_STARTING_WITH = "존재하지 않는 "; public static final String HAS_MESSAGE_ENDING_WITH = "id = "; @InjectMocks PostService postService; @Mock PostQueryRepository postQueryRepository; @Mock PostRepository postRepository; private Post getPost(String title, String content) { return Post.createPostBuilder() .title(title) .content(content) .build(); } @Test @DisplayName("post 목록 조회") void findPosts() { //given List<Post> posts = new ArrayList<>(); for (int i = 0; i < 30; i++) { posts.add(getPost(POST_TITLE + i, POST_CONTENT)); } //검색 안먹힘 PostSearchCondition condition = new PostSearchCondition(); condition.setSearchCondition(SearchCondition.TITLE); condition.setSearchKeyword("0"); PageRequest pageRequest = PageRequest.of(0, 10); given(postQueryRepository.findPosts(condition, pageRequest)).willReturn(new PageImpl<>(posts)); //when Page<Post> contents = postService.findPosts(condition, pageRequest); //then assertThat(contents.getTotalElements()).isEqualTo(3); assertThat(contents.getContent().size()).isEqualTo(3); } } 여기서 findPosts 단위 테스트를 진행하려고 하는데요.목록을 30개를 만들고 검색조건과 페이지 정보를 파라미터로 넘겨서 검색이 된 결과가 나올것이라고 예상했지만 검색조건은 먹히지 않고 30개의 목록만 리턴이 됩니다. 전 검색 조건대로 "0"들어간 title만 검색이되어서contents.getTotalElements() == 3, contents.getcontent().size() == 3 으로 예상을 했는데contents.getTotalElements() == 30, contents.getcontent().size() == 30 로 결과가 나와 테스트에 실패하게됩니다. 구글링으로 mockito, page, test, querydsl, parameter 등 다양하게 검색 해봤는데 원하는 결과를 얻기 못해 질문 한번 드려봅니다. 혹시나 제가 잘못된 방향으로 테스트를 진행하고 있는건지 혹은 다른 방법이 있을지 궁금합니다!깃저장소도 같이 공유드립니다.https://github.com/heechul90/heech-heechlog-server
- 미해결호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)
안녕하세요. 레포지토리 구성원 참여 부탁드립니다.
안녕하세요. 늦었지만 프로젝트 레포지토리 구성원 참여부탁드리겠습니다.강의 잘보고 있습니다. 감사합니다:)메일 : gyuseok6394@gmail.com