발생한 오류
Failed to load ApplicationContext
java.lang.IllegalStateException: Failed to load ApplicationContext
...
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'productRepositoryImpl' defined in file [C:\Users\21528463\IdeaProjects\mocosa\build\classes\java\main\com\hanghae99\mocosa\layer\repository\ProductRepositoryImpl.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.querydsl.jpa.impl.JPAQueryFactory' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
...
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.querydsl.jpa.impl.JPAQueryFactory' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
...
오류가 발생한 코드
@RequiredArgsConstructor
public class ProductRepositoryImpl implements ProductRepositoryCustom {
private final JPAQueryFactory queryFactory;
}
private final JPAQueryFactory queryFactory;
지워보자!@RequiredArgsConstructor
public class ProductRepositoryImpl implements ProductRepositoryCustom {
}
java.lang.IllegalStateException: No primary or single unique constructor found for interface java.awt.print.Pageable
@GetMapping("/api/search")
@ResponseBody
public Slice<SearchResponseDto> searchProduct(Pageable pageable,
@RequestParam(required = false, defaultValue = "리뷰순") String sort,
@RequestParam(required = false, defaultValue = "전체") String categoryFilter,
@RequestParam(required = false, defaultValue = "0") int minPriceFilter,
@RequestParam(required = false, defaultValue = "2147483647") int maxPriceFilter,
@RequestParam(required = false, defaultValue = "0") int reviewFilter,
@RequestParam String keyword) {
return productService.searchProduct(pageable, sort, categoryFilter, minPriceFilter, maxPriceFilter, reviewFilter);
}
@GetMapping("/api/search")
@ResponseBody
public Slice<SearchResponseDto> searchProduct(@RequestParam(required = false, defaultValue = "0") int page,
@RequestParam(required = false, defaultValue = "리뷰순") String sort,
@RequestParam(required = false, defaultValue = "전체") String categoryFilter,
@RequestParam(required = false, defaultValue = "0") int minPriceFilter,
@RequestParam(required = false, defaultValue = "2147483647") int maxPriceFilter,
@RequestParam(required = false, defaultValue = "0") int reviewFilter,
@RequestParam String keyword) {
return productService.searchProduct(page, sort, categoryFilter, minPriceFilter, maxPriceFilter, reviewFilter);
}
expected: <400 BAD_REQUEST> but was: <401 UNAUTHORIZED>
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
// implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.h2database:h2'
runtimeOnly 'mysql:mysql-connector-java'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
// 테스트 코드를 위한 Lombok 라이브러리
testCompileOnly 'org.projectlombok:lombok:1.18.12'
testAnnotationProcessor 'org.projectlombok:lombok:1.18.12'
// 3. querydsl dependencies 추가
implementation "com.querydsl:querydsl-jpa:${queryDslVersion}"
implementation "com.querydsl:querydsl-apt:${queryDslVersion}"
}
Type definition error: [simple type, class com.hanghae99.mocosa.integration.ProductIntegrationTest$SearchResponseDtos]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.hanghae99.mocosa.integration.ProductIntegrationTest$SearchResponseDtos` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("test")
public class ProductIntegrationTest {
@Autowired
private TestRestTemplate restTemplate;
@Test
@DisplayName("검색에 성공한 케이스 - 필터 제외")
public void case1(){
//given
String keyword = "무탠다드";
//when
ResponseEntity<SearchResponseDtoList> response = restTemplate
.getForEntity(
"/api/search?keyword="+keyword,
SearchResponseDtoList.class
);
//then
assertEquals(HttpStatus.OK, response.getStatusCode());
SearchResponseDtoList responseBody = response.getBody();
assertNotNull(responseBody);
assertEquals(
1L
,responseBody.content.get(0).productId);
assertEquals(
"릴렉스 핏 크루 넥 반팔 티셔츠"
,responseBody.content.get(0).name);
assertEquals(
"image.png"
,responseBody.content.get(0).thumbnail);
assertEquals(
"무신사 스탠다드"
,responseBody.content.get(0).brandName);
assertEquals(
"상의"
,responseBody.content.get(0).category);
assertEquals(
10690
,responseBody.content.get(0).price);
assertEquals(
100
,responseBody.content.get(0).amount);
assertEquals(
69058
,responseBody.content.get(0).reviewNum);
assertEquals(
4.8
,responseBody.content.get(0).reviewAvg);
}
@Getter
@Setter
@Builder
static class SearchResponseDtoList {
private List<SearchResponseDto> content;
}
@Getter
@Setter
@Builder
static class SearchResponseDto{
private Long productId;
private String name;
private String thumbnail;
private String brandName;
private String category;
private int price;
private int amount;
private int reviewNum;
private float reviewAvg;
}
}
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("test")
public class ProductIntegrationTest {
@Autowired
private TestRestTemplate restTemplate;
@Test
@DisplayName("검색에 성공한 케이스 - 필터 제외")
public void case1(){
//given
String keyword = "무탠다드";
//when
ResponseEntity<SearchResponseDtoList> response = restTemplate
.getForEntity(
"/api/search?keyword=" + keyword,
SearchResponseDtoList.class
);
//then
assertEquals(HttpStatus.OK, response.getStatusCode());
List<SearchResponseDto> responseBody = response.getBody().content;
assertNotNull(responseBody);
assertEquals(
1L
,responseBody.content.get(0).productId);
assertEquals(
"릴렉스 핏 크루 넥 반팔 티셔츠"
,responseBody.content.get(0).name);
assertEquals(
"image.png"
,responseBody.content.get(0).thumbnail);
assertEquals(
"무신사 스탠다드"
,responseBody.content.get(0).brandName);
assertEquals(
"상의"
,responseBody.content.get(0).category);
assertEquals(
10690
,responseBody.content.get(0).price);
assertEquals(
100
,responseBody.content.get(0).amount);
assertEquals(
69058
,responseBody.content.get(0).reviewNum);
assertEquals(
4.8
,responseBody.content.get(0).reviewAvg);
}
@Getter
@Setter
@Builder
static class SearchResponseDtoList {
private List<SearchResponseDto> content;
}
@Getter
@Setter
@Builder
static class SearchResponseDto{
private Long productId;
private String name;
private String thumbnail;
private String brandName;
private String category;
private int price;
private int amount;
private int reviewNum;
private float reviewAvg;
}
}
SearchResponseDtoList responseBody = response.getBody();
이고 해결한 코드는 List<SearchResponseDto> responseBody = response.getBody().content;
이렇게 반환 타입을 잘못 작성해서 난 오류였다. 다음부터는 반환타입을 잘 생각해서 코드를 작성하도록 하자!org.opentest4j.AssertionFailedError:
Expected :4.8
Actual : 4.800000190734863
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("test")
public class ProductIntegrationTest {
@Autowired
private TestRestTemplate restTemplate;
@Test
@DisplayName("검색에 성공한 케이스 - 필터 제외")
public void case1(){
//given
String keyword = "무탠다드";
//when
ResponseEntity<SearchResponseDtoList> response = restTemplate
.getForEntity(
"/api/search?keyword=" + keyword,
SearchResponseDtoList.class
);
//then
assertEquals(HttpStatus.OK, response.getStatusCode());
List<SearchResponseDto> responseBody = response.getBody().content;
assertNotNull(responseBody);
assertEquals(
1L
,responseBody.content.get(0).productId);
assertEquals(
"릴렉스 핏 크루 넥 반팔 티셔츠"
,responseBody.content.get(0).name);
assertEquals(
"image.png"
,responseBody.content.get(0).thumbnail);
assertEquals(
"무신사 스탠다드"
,responseBody.content.get(0).brandName);
assertEquals(
"상의"
,responseBody.content.get(0).category);
assertEquals(
10690
,responseBody.content.get(0).price);
assertEquals(
100
,responseBody.content.get(0).amount);
assertEquals(
69058
,responseBody.content.get(0).reviewNum);
assertEquals(
4.8
,responseBody.content.get(0).reviewAvg);
}
}
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("test")
public class ProductIntegrationTest {
static final double TOLERANCE = 0.001;
@Autowired
private TestRestTemplate restTemplate;
@Test
@DisplayName("검색에 성공한 케이스 - 필터 제외")
public void case1(){
//given
String keyword = "무탠다드";
//when
ResponseEntity<SearchResponseDtoList> response = restTemplate
.getForEntity(
"/api/search?keyword=" + keyword,
SearchResponseDtoList.class
);
//then
assertEquals(HttpStatus.OK, response.getStatusCode());
List<SearchResponseDto> responseBody = response.getBody().content;
assertNotNull(responseBody);
assertEquals(
1L
,responseBody.content.get(0).productId);
assertEquals(
"릴렉스 핏 크루 넥 반팔 티셔츠"
,responseBody.content.get(0).name);
assertEquals(
"image.png"
,responseBody.content.get(0).thumbnail);
assertEquals(
"무신사 스탠다드"
,responseBody.content.get(0).brandName);
assertEquals(
"상의"
,responseBody.content.get(0).category);
assertEquals(
10690
,responseBody.content.get(0).price);
assertEquals(
100
,responseBody.content.get(0).amount);
assertEquals(
69058
,responseBody.content.get(0).reviewNum);
assertEquals(
4.8
,responseBody.content.get(0).reviewAvg, TOLERANCE);
}
}