반응형
가독성 높이는 습관 - 테스트 코드 - 실습
실습
- 상태 테스트
- 행위 테스트
- 성송 & 실패 테스트
package com.example.zerobase.domain;
import com.example.zerobase.type.ZerobaseCourseStatus;
import com.example.zerobase.web.exception.ExceptionCode;
import com.example.zerobase.web.exception.ZerobaseException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
public class ZerobaseCourseQueryService {
private final ZerobaseCourseRepository zerobaseCourseRepository;
public ZerobaseCourse getOrThrow(Long id) {
return zerobaseCourseRepository.findById(id)
.filter(it -> !it.isHidden())
// TODO 적당히 Exception을 바꿔보세요
.orElseThrow(() -> new ZerobaseException(ExceptionCode.NOT_FOUND_COURSE));
}
public List<ZerobaseCourse> getZerobaseCourseList(ZerobaseCourseStatus status) {
checkStatus(status);
return zerobaseCourseRepository.findAll()
.stream()
.filter(it -> !it.isHidden())
.filter(it -> it.isSameStatus(status))
.collect(Collectors.toList());
}
private void checkStatus(ZerobaseCourseStatus status) {
if (status.isUnknown())
// TODO 적당히 Exception을 바꿔보세요
throw new ZerobaseException(ExceptionCode.INVALID_COURSE_STATUS);
if (status.isClose())
// TODO 적당히 Exception을 바꿔보세요
throw new ZerobaseException(ExceptionCode.ALREADY_CLOSED);
}
}
package com.example.zerobase.web.advice;
import com.example.zerobase.web.exception.ExceptionCode;
import com.example.zerobase.web.exception.ZerobaseException;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class GlobalExceptionHandler {
// TODO : 익셉션 핸들러를 작성하세요
@ExceptionHandler(value = ZerobaseException.class)
public ResponseEntity<ErrorResult> runtimeException(ZerobaseException e) {
ExceptionCode errorCode = e.getCode();
ErrorResult result = ErrorResult.builder()
.code(errorCode.name())
.message(e.getMessage())
.build();
return new ResponseEntity(result, errorCode.getStatus()); }
}
package com.example.zerobase.web;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.filter.CharacterEncodingFilter;
import static com.example.zerobase.web.exception.ExceptionCode.*;
import static org.hamcrest.Matchers.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
@ActiveProfiles("test")
@SpringBootTest
@AutoConfigureMockMvc
public class ZeroBaseCourseControllerTest {
private MockMvc mockMvc;
@BeforeEach
void setUp(WebApplicationContext ctx) {
this.mockMvc = MockMvcBuilders.webAppContextSetup(ctx)
.addFilters(new CharacterEncodingFilter("UTF-8", true))
.alwaysDo(print())
.build();
}
@DisplayName("❗️강의 단건조회 api 테스트")
@ParameterizedTest(name = "/v1/api/course/{arguments} 테스트")
@ValueSource(longs = {1, 2, 3, 4, 5})
public void success__getZerobaseCourse(long id) throws Exception {
mockMvc.perform(get("/v1/api/course/" + id).contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id", notNullValue()))
.andExpect(jsonPath("$.name", notNullValue()))
.andExpect(jsonPath("$.start_at", notNullValue()))
.andExpect(jsonPath("$.end_at", notNullValue()));
}
@DisplayName("❗강의 단건조회 실패 api 테스트")
@ParameterizedTest(name = "/v1/api/course/{arguments} 테스트")
@ValueSource(longs = {-1, 0, 6, 7, Long.MAX_VALUE})
public void fail__getZerobaseCourse(long id) {
assertThatThrownBy(() -> mockMvc.perform(get("/v1/api/course/" + id).contentType(MediaType.APPLICATION_JSON))
.andExpect(status().is5xxServerError())
.andExpect(jsonPath("$.code", is(NOT_FOUND_COURSE.name())))
.andExpect(jsonPath("$.message", is(NOT_FOUND_COURSE.getMessage()))));
}
@DisplayName("❗강의 다건조회 api 테스트")
@ParameterizedTest(name = "/v1/api/course?status={arguments} 테스트")
@ValueSource(strings = {"OPEN", "IN_PROGRESS"})
public void success__getZerobaseCourses(String status) throws Exception {
mockMvc.perform(get("/v1/api/course?status=" + status).contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.*", hasSize(2)))
.andExpect(jsonPath("$.[*].status", Matchers.everyItem(is(status))));
}
@DisplayName("❗강의 다건조회 실패 api 테스트(CLOSE)")
@ParameterizedTest(name = "/v1/api/course?status=CLOSE 테스트")
@ValueSource(strings = {"CLOSE"})
public void fail____getZerobaseCourses() {
assertThatThrownBy(() -> mockMvc.perform(get("/v1/api/course?status=CLOSE").contentType(MediaType.APPLICATION_JSON))
.andExpect(status().is5xxServerError())
.andExpect(jsonPath("$.code", is(ALREADY_CLOSED.name())))
.andExpect(jsonPath("$.message", is(ALREADY_CLOSED.getMessage()))));
}
@DisplayName("❗강의 다건조회 실패 api 테스트(INVALID)")
@ParameterizedTest(name = "/v1/api/course?status={arguments} 테스트")
@ValueSource(strings = {"INVALID", "HELLO", "FOO", "", "1111"})
public void fail____getZerobaseCourses(String status) {
assertThatThrownBy(() -> mockMvc.perform(get("/v1/api/course?status=" + status).contentType(MediaType.APPLICATION_JSON))
.andExpect(status().is4xxClientError())
.andExpect(jsonPath("$.code", is(INVALID_COURSE_STATUS.name())))
.andExpect(jsonPath("$.message", is(INVALID_COURSE_STATUS.getMessage()))));
}
}
반응형
'cs > java-spring-boot' 카테고리의 다른 글
[Zero-base] 12-2. 서비스 중 기능 추가, 변경 시 고려해야 할 것들 (0) | 2022.03.22 |
---|---|
[Zero-base] 12-1. 현업에서 마주하는 실제 요청은? (0) | 2022.03.22 |
[Zero-base] 10-4. 테스트 코드 (2) (0) | 2022.03.18 |
[Zero-base] 10-3. 테스트 코드 (1) (0) | 2022.03.18 |
[Zero-base] 10-2. 익셉션 핸들링 (2) - 실습 (0) | 2022.03.18 |