반응형
테스트 대상 Controller
@RestController
public class BlogSearchController {
private final SearchBlogUseCase searchBlogUseCase;
public BlogSearchController(SearchBlogUseCase searchBlogUseCase) {
this.searchBlogUseCase = searchBlogUseCase;
}
@GetMapping("/api/v1/blogs")
public ResultResponse<BlogDto> searchBlogs(
@Valid SearchBlogRequestBody requestBody,
@RequestParam(required = false, defaultValue = "1") int page,
@RequestParam(required = false, defaultValue = "10") int size
) {
var command = BlogSearchCommand.builder()
.keyword(requestBody.getKeyword())
.url(requestBody.getUrl())
.sort(requestBody.getSort())
.page(page)
.size(size)
.build();
return ResultResponse.ok(searchBlogUseCase.search(command));
}
}
의존성 추가하기
asciidoctor 는 restdocs 로 만들어지는 adoc 파일을 HTML 등과 같은 파일로 파싱해주는 역할을 한다.
plugins {
id("com.epages.restdocs-api-spec") version Versions.restdocsApiSpec apply false
id("org.asciidoctor.jvm.convert") version Versions.asciidoctorPlugin apply false
}
dependencies {
integrationImplementation("org.springframework.boot:spring-boot-starter-test")
integrationImplementation("org.springframework.restdocs:spring-restdocs-mockmvc")
integrationImplementation("io.rest-assured:spring-mock-mvc")
}
설정 파일 만들기
@SpringBootTest
@ExtendWith({RestDocumentationExtension.class})
public abstract class BlogApiTest {
@Autowired
WebApplicationContext webApplicationContext;
@BeforeEach
void setup(RestDocumentationContextProvider restDocumentation) {
RestAssuredMockMvc.webAppContextSetup(
webApplicationContext,
documentationConfiguration(restDocumentation)
);
}
protected OperationRequestPreprocessor defaultPreprocessRequest() {
return preprocessRequest(
prettyPrint()
);
}
protected OperationResponsePreprocessor defaultPreprocessResponse() {
return preprocessResponse(
prettyPrint()
);
}
}
API 테스트 작성하기
@ExtendWith(MockitoExtension.class)
public class BlogSearchControllerTest extends BlogApiTest {
@Mock
private SearchBlogUseCase searchBlogUseCase;
@BeforeEach
void setup(RestDocumentationContextProvider restDocumentation) {
var blogSearchController = new BlogSearchController(searchBlogUseCase);
standaloneSetup(
MockMvcBuilders
.standaloneSetup(blogSearchController)
.apply(documentationConfiguration(restDocumentation))
);
}
@Test
void searchBlogs() {
when(searchBlogUseCase.search(any()))
.thenReturn(getMockBlogDto());
var keyword = "키워드";
var response = given()
.accept(ContentType.JSON)
.param("keyword", keyword)
.param("page", 1)
.param("size", 10)
.when()
.get("/api/v1/blogs");
response.prettyPrint();
response.then()
.status(HttpStatus.OK)
.apply(
document(
"search-blogs",
resourceDetails().tag("블로그").description("블로그 조회 (페이징)"),
defaultPreprocessRequest(),
defaultPreprocessResponse(),
requestParameters(
parameterWithName("keyword").description("블로그 검색 키워드"),
parameterWithName("url").description("블로그 검색 주소").optional(),
parameterWithName("sort").description("정렬 기준 [정확도순: ACCURACY, 최신순: RECENCY]").optional(),
parameterWithName("page").description("전시 유무").optional(),
parameterWithName("size").description("페이지 번호").optional()
),
responseFields(
fieldWithPath("success")
.type(JsonFieldType.BOOLEAN)
.description("성공 여부"),
fieldWithPath("code")
.type(JsonFieldType.STRING)
.description("code"),
fieldWithPath("message")
.type(JsonFieldType.STRING)
.description("message")
.optional(),
fieldWithPath("data")
.type(JsonFieldType.OBJECT)
.description("데이터"),
fieldWithPath("data.documents[]")
.type(JsonFieldType.ARRAY)
.description("블로그 문서 목록"),
fieldWithPath("data.documents[].title")
.type(JsonFieldType.STRING)
.description("블로그 제목"),
fieldWithPath("data.documents[].contents")
.type(JsonFieldType.STRING)
.description("블로그 내용"),
fieldWithPath("data.documents[].url")
.type(JsonFieldType.STRING)
.description("블로그 주소"),
fieldWithPath("data.documents[].blogName")
.type(JsonFieldType.STRING)
.description("블로그 이름"),
fieldWithPath("data.documents[].thumbnail")
.type(JsonFieldType.STRING)
.description("이미지 썸네일"),
fieldWithPath("data.documents[].writtenAt")
.type(JsonFieldType.STRING)
.description("블로그 글 작성 시간"),
subsectionWithPath("data.pagination")
.type(JsonFieldType.OBJECT)
.description("페이지네이션")
)
)
);
}
private BlogDto getMockBlogDto() {
var fixtureMonkey = FixtureMonkey.builder()
.putGenerator(BlogDocumentDto.class, BuilderArbitraryGenerator.INSTANCE)
.build();
var sampleBlogs = fixtureMonkey.giveMeBuilder(BlogDocumentDto.class)
.setNotNull("title")
.setNotNull("contents")
.setNotNull("url")
.setNotNull("blogName")
.setNotNull("thumbnail")
.setNotNull("writtenAt")
.sampleList(10);
return BlogDto.builder()
.documents(sampleBlogs)
.pagination(Pagination.empty())
.build();
}
}
참고자료
FixtureMonkey 관련
https://mangchhe.github.io/test/2022/07/12/FixtureMonkey/
반응형
'스프링' 카테고리의 다른 글
[스프링] 멀티 모듈 환경에서 application.yml 사용하기 (0) | 2023.02.09 |
---|---|
[스프링] @Valid 사용과 테스트 케이스 작성하기 (0) | 2023.02.06 |
[스프링] JPA 관련 통합 테스트 환경 구축하기 (0) | 2023.02.06 |
[스프링] JUnit5, AssertJ, Mockito 기반 테스트 환경 구축하기 (0) | 2023.02.06 |
[스프링] 스프링에서 관리하는 자바 객체, 빈 (Bean) (0) | 2023.02.03 |
댓글