시간 순서대로 발생한 일을 얘기해보겠다
때는 바야흐로 프로젝트의 API에 대한 테스트 코드를 작성하던 때였다
여섯개의 컨트롤러가 있지만 우선 두 개의 컨트롤러 메서드에 대한 테스트를 작성해보았다.
음식점을 초기에 등록 요청을 하는 API는 그럭저럭 테스트 작성을 잘했다. 이를통해 약간의 자신감을 얻었다.
여기에서 간단한 내용을 확인해 볼 수 있다ㅎㅎ
문제는 다음 테스트를 작성하는 중에 나오게 된다. 자신감이 자만이된 순간이라 생각한다ㅋㅋㅋ
우선 API의 요구사항은 '음식 주문'이다. 사용자가 선택한 특정 음식점의 번호와 하나 이상의 음식의 번호와 수량으로 음식 주문을 하는 것이다.
@AutoConfigureMockMvc
@SpringBootTest
class OrderControllerV1Test {
@Autowired
MockMvc mockMvc;
@Autowired
EntityManager em;
@Autowired
RestaurantRepository restaurantRepository;
@Autowired
FoodRepository foodRepository;
@Autowired
OrderRepository orderRepository;
@BeforeEach
void clear() {
restaurantRepository.deleteAll();
foodRepository.deleteAll();
orderRepository.deleteAll();
}
@Test
@Transactional
@DisplayName("주문_음식_요청_테스트")
void saveOrder() throws Exception {
// given
Restaurant savedRestaurant = restaurantRepository.save(new Restaurant("쉐이크쉑 청담점", 5000, 2000));
foodRepository.save(new Food(savedRestaurant, "쉐이크쉑 버거", "10900"));
foodRepository.save(new Food(savedRestaurant, "치즈 감자튀김", "4900"));
foodRepository.save(new Food(savedRestaurant, "쉐이크", "5900"));
List<OrderFoodInfoDto> foods = new ArrayList<>();
foods.add(new OrderFoodInfoDto(1L, 1));
foods.add(new OrderFoodInfoDto(2L, 2));
foods.add(new OrderFoodInfoDto(3L, 3));
OrderFoodRequestDto request = OrderFoodRequestDto.builder()
.restaurantId(1L)
.foods(foods)
.build();
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(request);
// when
mockMvc.perform(post("/api/v1/orders")
.contentType(APPLICATION_JSON)
.content(json)
)
.andExpect(status().isOk())
.andDo(print());
em.flush();
em.clear();
// then
//assertEquals(1L, orderRepository.count());
Order order = orderRepository.findAll().get(0);
int totalPrice = 0;
List<OrderFood> orderFoodList = order.getOrderFoods();
for (OrderFood orderFood : orderFoodList) {
Food food = orderFood.getFood();
totalPrice += ( Integer.parseInt(food.getPrice()) * orderFood.getQuantity() );
}
assertEquals(totalPrice + order.getRestaurant().getDeliveryFee(), 40400);
}
}
테스트 진행을 위해서는 '음식점 기본 정보'와 '음식점의 음식' 정보가 DB에 저장되어 있어야 한다
이후 MockMvc를 이용하여 요청이 성공적으로 들어가는지 알아보기 위하여 요청 데이터를 만들어야 했다. 따라서 실제 컨트롤러의 메서드에서 받는 DTO를 기반으로 데이터를 만들고 'ObjectMapper'를 이용하여 JSON으로 만들어준다.
테스트는 성공적으로 동작한다... 그러나 API요구 명세서 상의 요청 예시와 테스트 상의 결과가 달랐다.
테스트의 결과는 다음과 같다
설마 테스트 코드를 디버깅할까 싶었지만...(현업에서 하는지 모르겠지만...) 우선 디버깅을 해보았다 그런데 위에 코드에서 objectMapper.writeValueAsString(request) 이후 '음식번호'와 '음식수량'에 관한 리스트가 다른 이름으로 2개 들어가 있었다.
이미지가 잘 보이지 않는다;; 아래와 같다 'foods'와 'orderFoodInfoList'를 유심히 보자!
{"restaurantId":1,"foods":[{"id":1,"quantity":1},{"id":2,"quantity":2},{"id":3,"quantity":3}],"orderFoodInfoList":[{"id":1,"quantity":1},{"id":2,"quantity":2},{"id":3,"quantity":3}]}
@Getter // 클래스명 변경 FoodOrderRequestDto -> OrderFoodRequestDto
@NoArgsConstructor
public class OrderFoodRequestDto {
private Long restaurantId;
private List<OrderFoodInfoDto> foods;
public List<OrderFoodInfoDto> getOrderFoodInfoList() {
return foods;
}
}
Unable to evaluate the expression Method threw 'org.hibernate.LazyInitializationException' ~
'org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role' ~~
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "ORDER_ID")
private Long id;
@ManyToOne(fetch = LAZY)
@JoinColumn(name = "RESTAURANT_ID")
private Restaurant restaurant;
@OneToMany(fetch = LAZY, mappedBy = "order")
List<OrderFood> OrderFoods = new ArrayList<>();
public Order(Restaurant restaurant) {
this.restaurant = restaurant;
}
}