REST API with SpringBoot(3)
작성일
Event 생성 API 구현: Bad Request 응답 본문 만들기
serialization: ‘객체 → json’ 으로 변환
deserialization: ‘json → 객체’ 로 변환
body에 Bad Request에 대한 응답을 넣고 싶은데 관련 에러는 Errors에 담겨 있다
그런데, body에 error를 담으려고 하면 에러가 발생한다!
원인은 error를 json으로 변환할 수 없기 때문에..
그렇다면 왜 event 객체는 body에 담을 수 있었을까?
→ event는 objectMapper를 사용해서 객체에서 json으로 변환하는데 이때 bean serialization을 사용해서 자바빈 스팩을 준수했기 때문에 변환할수 있었던것..
따라서, 아래 코드와 같이 error를 serialization 해주는 코드를 작성해야한다.
package me.test.demoinflearnrestapi.common;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import org.springframework.boot.jackson.JsonComponent;
import org.springframework.validation.Errors;
import java.io.IOException;
@JsonComponent
public class ErrorsSerializer extends JsonSerializer<Errors> {
@Override
public void serialize(Errors errors, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeStartArray();
errors.getFieldErrors().forEach(e -> {
try {
jsonGenerator.writeStartObject();
jsonGenerator.writeStringField("field", e.getField());
jsonGenerator.writeStringField("objectName", e.getObjectName());
jsonGenerator.writeStringField("code", e.getCode());
jsonGenerator.writeStringField("defaultMessage", e.getDefaultMessage());
Object rejectedValue = e.getRejectedValue();
if (rejectedValue != null) {
jsonGenerator.writeStringField("rejectedValue", rejectedValue.toString());
}
jsonGenerator.writeEndObject();
} catch (IOException e1) {
e1.printStackTrace();
}
});
errors.getGlobalErrors().forEach(e -> {
try {
jsonGenerator.writeStartObject();
jsonGenerator.writeStringField("objectName", e.getObjectName());
jsonGenerator.writeStringField("code", e.getCode());
jsonGenerator.writeStringField("defaultMessage", e.getDefaultMessage());
jsonGenerator.writeEndObject();
} catch (IOException e1) {
e1.printStackTrace();
}
});
jsonGenerator.writeEndArray();
}
}
@JsonComponent
ObjectMapper 에 Custom Serializer를 등록해 주어야하는데 Spring Boot에서 제공하는 @JsonComponent
를 사용하면 손쉽게 등록이 가능하다.
Event 생성 API 구현: 비즈니스 로직 적용
public void update() {
// Update free
if (this.basePrice == 0 && this.maxPrice == 0) {
this.free = true;
} else {
this.free = false;
}
// Update offline
if (this.location == null || this.location.isBlank()) {
this.offline = false;
} else {
this.offline = true;
}
}
entity test code 작성
@Test
public void testFree() {
// Given
Event event = Event.builder()
.basePrice(0)
.maxPrice(0)
.build();
// When
event.update();
// Then
assertThat(event.isFree()).isTrue();
// Given
event = Event.builder()
.basePrice(100)
.maxPrice(0)
.build();
// When
event.update();
// Then
assertThat(event.isFree()).isFalse();
// Given
event = Event.builder()
.basePrice(0)
.maxPrice(100)
.build();
// When
event.update();
// Then
assertThat(event.isFree()).isFalse();
}
위의 코드처럼 작성해도 되지만 중복이 발생하기 때문에 보기에 좋지 않다
JUnitParams
를 이용하여 중복코드를 줄이고 테스트코드를 작성할 수 있다.
Event 생성 API 구현: 매개변수를 이용한 테스트
dependency 추가
pom.xml
<dependency>
<groupId>pl.pragmatists</groupId>
<artifactId>JUnitParams</artifactId>
<version>1.1.1</version>
<scope>test</scope>
</dependency>
package me.test.demoinflearnrestapi.events;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.assertj.core.api.Assertions.assertThat;
@RunWith(JUnitParamsRunner.class)
public class EventTest {
...
//parametersFor: 컨벤션
private Object[] parametersForTestFree() {
return new Object[]{
new Object[]{0, 0, true},
new Object[]{100, 0, false},
new Object[]{0, 100, false},
new Object[]{100, 200, false}
};
}
@Test
@Parameters(method = "parametersForTestFree")
public void testFree(int basePrice, int maxPrice, boolean isFree) {
// Given
Event event = Event.builder()
.basePrice(basePrice)
.maxPrice(maxPrice)
.build();
// When
event.update();
// Then
assertThat(event.isFree()).isEqualTo(isFree);
}
private Object[] parametersForTestOffline() {
return new Object[]{
new Object[]{"강남", true},
new Object[]{null, false},
new Object[]{"", false}
};
}
@Test
@Parameters
public void testOffline(String location, boolean isOffline) {
// Given
Event event = Event.builder()
.location(location)
.build();
// When
event.update();
// Then
assertThat(event.isOffline()).isEqualTo(isOffline);
}
}
parametersFor테스트코드명
→ 이렇게 작성하면 알아서 맵핑해준다.(코드 컨벤션, 규약)