Table of Contents
Spring Boot Test 샘플 코드
Spring Boot Test 클래스를 생성할 때마다 반복적으로 삽질을 하여,
샘플 코드를 적어 놓는다.
Test Class
org.junit.jupiter.api.Test
가 임포트 되어 있는지 확인한다.
IntelliJ 가 엄한 클래스를 임포트해서 고생이 많다.
package kr.pe.skyer9.mgapiserver.web;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
// ......
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
// ......
import static org.junit.jupiter.api.Assertions.assertEquals;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class UserInfoControllerTest {
@LocalServerPort
private int port;
HttpHeaders headers;
ObjectMapper mapper;
@Autowired
private TestRestTemplate restTemplate;
@BeforeEach
public void setUp() {
mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
headers = new HttpHeaders();
}
@Test
public void createNewUser() throws URISyntaxException, JsonProcessingException {
// given
String key_uuid = Util.getRandomString(32);
URI uri = new URI(getBaseUrl() + "create");
UserInfoDto dto = new UserInfoDto();
dto.setKeyUuid(key_uuid);
HttpEntity<UserInfoDto> request = new HttpEntity<>(dto);
// when
ResponseEntity<String> result = this.restTemplate.exchange(uri, HttpMethod.POST, request, String.class);
// then
assertEquals(200, result.getStatusCodeValue());
ApiResponseWithData apiResponseWithData = mapper.readValue(result.getBody(), ApiResponseWithData.class);
UserInfoDto saved = mapper.convertValue(apiResponseWithData.getData(), UserInfoDto.class);
assertEquals(key_uuid, saved.getKeyUuid());
}
@Test
public void updateUser() throws URISyntaxException, JsonProcessingException {
// given
String accessToken = "CvMgV52Uz8tHj8HaX644TjXXXXXXXXXX";
String key_uuid = "rMPzBha4d9JXaXXXXXXXXXXXXXXXXX";
String userName = "Lee San";
String userEmail = "skyer9@gmail.com";
URI uri = new URI(getBaseUrl() + "update");
UserInfoDto dto = new UserInfoDto();
dto.setKeyUuid(key_uuid);
dto.setUserName(userName);
dto.setAccessToken(accessToken);
// when
headers.add(Constant.X_Authorization, accessToken);
HttpEntity<UserInfoDto> request = new HttpEntity<>(dto, headers);
ResponseEntity<String> result = this.restTemplate.exchange(uri, HttpMethod.POST, request, String.class);
// then
assertEquals(200, result.getStatusCodeValue());
ApiResponseMessage apiResponseMessage = mapper.readValue(result.getBody(), ApiResponseMessage.class);
assertEquals("000", apiResponseMessage.getCode());
}
private String getBaseUrl() {
return "http://localhost:" + port + "/v1/user/";
}
}
RestController
@RestController
@RequestMapping("/v1/user")
public class UserInfoController extends BaseRestController<UserInfoDto, UserInfo, String, String> {
public UserInfoController(UserInfoService service) {
super(service);
}
@PostMapping("/create")
public ResponseEntity<?> createNewUser(@RequestBody UserInfoDto dto) {
UserInfoService service = (UserInfoService) super.service;
return ResponseEntity.ok(new ApiResponseWithData(ResponseCode.OK, service.createNewUser(dto)));
}
@PostMapping("/update")
public ResponseEntity<?> updateUser(@RequestHeader(Constant.X_Authorization) String auth, @RequestBody UserInfoDto dto) {
UserInfoService service = (UserInfoService) super.service;
UserInfo saved = service.get(dto.getKeyUuid(), true);
if (!Objects.equals(auth, saved.getAccessToken())) {
throw new InvalidAccessTokenException("잘못된 접근입니다.");
}
return update(dto.getKeyUuid(), dto);
}
}
public class BaseRestController<D, E, KD, KE> {
protected final CustomGenericService<D, E, KD, KE> service;
public BaseRestController(CustomGenericService<D, E, KD, KE> service) {
this.service = service;
}
// PK 자동생성
public ResponseEntity<?> create(D dto) {
D created = service.create(dto);
return ResponseEntity.ok(new ApiResponseWithData(ResponseCode.OK, created));
}
// PK 수동부여(PK 체크)
public ResponseEntity<?> create(KD kd, D dto) {
D created = service.create(kd, dto);
return ResponseEntity.ok(new ApiResponseWithData(ResponseCode.OK, created));
}
public ResponseEntity<?> update(KD keyDto, D dto) {
service.update(keyDto, dto);
return ResponseEntity.ok(new ApiResponseMessage(ResponseCode.OK));
}
public ResponseEntity<?> select(KD keyDto) {
D dto = service.select(keyDto);
return ResponseEntity.ok(new ApiResponseWithData(ResponseCode.OK, dto));
}
public ResponseEntity<?> search(Map<String, String> params) {
SearchResponseDto searchResponseDto = service.search(params);
return ResponseEntity.ok(
new ApiResponseWithPaging(ResponseCode.OK,
searchResponseDto.getResults(),
searchResponseDto.getPageable(),
searchResponseDto.getTotalCount()
)
);
}
}
CustomGenericService
public abstract class CustomGenericService<D, E, KD, KE> {
protected JpaRepository<E, KE> repository;
protected final String title;
public CustomGenericService(JpaRepository<E, KE> repository, String title) {
this.repository = repository;
this.title = title;
}
// 생성
@Transactional
public D create(D d) {
E e = newEntity();
updateFromDto(d, e);
return toDto(repository.save(e));
}
// 체크 및 생성
@Transactional
public D create(KD id, D d) throws DataExistsException {
KE ke = toKeyEntity(id);
if (get(ke, false) != null) {
throw new DataExistsException(title + "이(가) 이미 존재합니다.");
}
E e = newEntity(ke);
updateFromDto(d, e);
return toDto(repository.save(e));
}
// 수정
@Transactional
public void update(KD id, D d) {
E e = get(toKeyEntity(id), true);
updateFromDto(d, e);
repository.save(e);
}
// 수정 또는 생성
@Transactional
public void updateOrCreate(KD id, D d) {
E e = get(toKeyEntity(id));
updateFromDto(d, e);
repository.save(e);
}
// DTO 조회
@Transactional(readOnly = true)
public D select(KD id) {
return toDto(get(toKeyEntity(id), true));
}
// Entity 반환
@Transactional
public E get(KE id, boolean throwExceptionIfNotExists) {
E e = repository.findById(id).orElse(null);
if (throwExceptionIfNotExists && (e == null)) {
throw new DataNotFoundException(title + "이(가) 존재하지 않습니다.");
} else {
return e;
}
}
@Transactional
public E get(KE id) {
Optional<E> o = repository.findById(id);
return o.orElseGet(() -> newEntity(id));
}
public abstract SearchResponseDto search(Map<String, String> params);
protected List<D> toDto(List<E> lst) {
return lst.stream().map(this::toDto).collect(Collectors.toList());
}
protected abstract D toDto(E e);
protected abstract E toEntity(D e);
// protected abstract KD toKeyDto(KE e);
protected abstract KE toKeyEntity(KD e);
protected abstract void updateFromDto(D d, E e);
protected abstract E newEntity();
protected abstract E newEntity(KE e);
}
ApiResponseMessage
@ToString
@Getter
@Setter
@NoArgsConstructor
public class ApiResponseMessage implements Serializable {
private String code;
private String message;
public ApiResponseMessage(ResponseCode responseCode) {
this.code = responseCode.getKey();
this.message = responseCode.getTitle();
}
public ApiResponseMessage(ResponseCode responseCode, String message) {
this.code = responseCode.getKey();
this.message = message;
}
}
ApiResponseWithData
@ToString
@Getter
@Setter
@NoArgsConstructor
public class ApiResponseWithData extends ApiResponseMessage implements Serializable {
private Object data;
public ApiResponseWithData(ResponseCode responseCode, Object data) {
super(responseCode);
this.data = data;
}
}
ApiResponseWithPaging
@ToString
@Getter
@Setter
@NoArgsConstructor
public class ApiResponseWithPaging extends ApiResponseWithData implements Serializable {
private ApiResponsePaging paging;
public ApiResponseWithPaging(ResponseCode responseCode, Object data) {
super(responseCode, data);
this.paging = ApiResponsePaging.builder()
.pageNo(0)
.pageSize(Constant.DEFAULT_PAGE_SIZE_10)
.totalCount(0)
.build();
}
public ApiResponseWithPaging(ResponseCode responseCode, List<Object> data, Pageable pageable, int totalCount) {
super(responseCode, data);
this.paging = ApiResponsePaging.builder()
.pageNo(pageable.getPageNumber())
.pageSize(pageable.getPageSize())
.totalCount(totalCount)
.build();
}
}