/test/~/interfaces/RestaurantControllerTest.java
package kr.co.fastcampus.eatgo.interfaces;
import kr.co.fastcampus.eatgo.domain.RestaurantRepository;
import kr.co.fastcampus.eatgo.domain.RestaurantRepositoryImpl;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import static org.hamcrest.core.StringContains.containsString;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringRunner.class) // api 요청처리 테스트를 해주기위한 스프링자체의 어노테이션
@WebMvcTest(RestaurantController.class) // 특정 컨트롤러를 테스트해준다는것을 명시하는 어노테이션
public class RestaurantControllerTest {
@Autowired
private MockMvc mvc; // MockMvc생성
@SpyBean(RestaurantRepositoryImpl.class) // 아래의 인터페이스가 실질적으로 어떤 클래스를 구현하는지 명시해줘야한다.
private RestaurantRepository restaurantRepository;
@Test
public void list() throws Exception {
mvc.perform(get("/restaurants"))
.andExpect(status().isOk())
.andExpect(content().string(containsString("\"id\":1004")))
.andExpect(content().string(containsString("\"name\":\"Bob zip\"")));
}
@Test
public void detail() throws Exception {
mvc.perform(get("/restaurants/1004"))
.andExpect(status().isOk())
.andExpect(content().string(containsString("\"id\":1004")))
.andExpect(content().string(containsString("\"name\":\"Bob zip\"")))
.andExpect(content().string(
containsString(("Kimchi")) // 메뉴추가 테스트
));
mvc.perform(get("/restaurants/2020"))
.andExpect(status().isOk())
.andExpect(content().string(containsString("\"id\":2020")))
.andExpect(content().string(containsString("\"name\":\"Cyber Food\"")));
}
}
/main/~/interfaces/RestaurantController.java
package kr.co.fastcampus.eatgo.interfaces;
import kr.co.fastcampus.eatgo.domain.MenuItem;
import kr.co.fastcampus.eatgo.domain.Restaurant;
import kr.co.fastcampus.eatgo.domain.RestaurantRepository;
import kr.co.fastcampus.eatgo.domain.RestaurantRepositoryImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class RestaurantController {
@Autowired // 객체를 따로 생성안해도 @Component가 붙어있는 클래스를 찾아 자동으로 의존주입을하게된다.
private RestaurantRepository repository;
@GetMapping("/restaurants") // 가게목록 반환 api
public List<Restaurant> list(){
List<Restaurant> restaurants = repository.findAll();
return restaurants;
}
@GetMapping("/restaurants/{id}") // 특정 가게상세 반환 api
public Restaurant detail(@PathVariable("id") Long id){
Restaurant restaurant = repository.findById(id);
restaurant.addMenuItem(new MenuItem("Kimchi")); // Kimchi 메뉴추가
return restaurant;
}
}
/main/~/domain/Restaurant.java
package kr.co.fastcampus.eatgo.domain;
import jdk.internal.jimage.ImageStrings;
import java.util.ArrayList;
import java.util.List;
public class Restaurant {
private final Long id;
private final String name;
private final String address;
private List<MenuItem> menuItems = new ArrayList<MenuItem>(); // 가게메뉴들을 담을 리스트 생성
public Restaurant(Long id, String name, String address) {
this.id = id;
this.name = name;
this.address = address;
}
public Long getId() {
return id;
}
public String getName() { // 생성된 객체의 이름 반환
return name;
}
public String getAddress() { // 생성된 객체의 주소 반환
return address;
}
public String getInformaion() { // 생성된 객체의 정보 반환
return name + " in " + address;
}
public List<MenuItem> getMenuItems() { // 가게의 메뉴들 반환
return menuItems;
}
public void addMenuItem(MenuItem menuItem) { // 가게메뉴 리스트에 가게메뉴 추가 메서드
menuItems.add(menuItem);
}
}
/main/~/domain/MenuItem.java 생성
package kr.co.fastcampus.eatgo.domain;
public class MenuItem {
private final String name;
public MenuItem(String name) { // 생성자
this.name = name;
}
public String getName(){
return name;
}
}
정상작동하는것을 확인할 수 있다.
현재 컨트롤러 로직을 보면 특정 가게의 정보를 얻어온 다음 다시 가게의 메뉴를 추가하는것을 확인 할 수 있다.
이제 이 대신 더 효율적으로 관리하기위해 우리가 메뉴 아이템들을 전체적으로 관리할 수 있는 레파지토리를 하나 더 생성할것이다.
/test/~/interfaces/RestaurantControllerTest.java
package kr.co.fastcampus.eatgo.interfaces;
import kr.co.fastcampus.eatgo.domain.MenuItemRepository;
import kr.co.fastcampus.eatgo.domain.MenuItemRepositoryImpl;
import kr.co.fastcampus.eatgo.domain.RestaurantRepository;
import kr.co.fastcampus.eatgo.domain.RestaurantRepositoryImpl;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import static org.hamcrest.core.StringContains.containsString;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringRunner.class) // api 요청처리 테스트를 해주기위한 스프링자체의 어노테이션
@WebMvcTest(RestaurantController.class) // 특정 컨트롤러를 테스트해준다는것을 명시하는 어노테이션
public class RestaurantControllerTest {
@Autowired
private MockMvc mvc; // MockMvc생성
@SpyBean(RestaurantRepositoryImpl.class) // 아래의 인터페이스가 실질적으로 어떤 클래스를 구현하는지 명시해줘야한다.
private RestaurantRepository restaurantRepository;
@SpyBean(MenuItemRepositoryImpl.class)
private MenuItemRepository menuItemRepository;
@Test
public void list() throws Exception {
mvc.perform(get("/restaurants"))
.andExpect(status().isOk())
.andExpect(content().string(containsString("\"id\":1004")))
.andExpect(content().string(containsString("\"name\":\"Bob zip\"")));
}
@Test
public void detail() throws Exception {
mvc.perform(get("/restaurants/1004"))
.andExpect(status().isOk())
.andExpect(content().string(containsString("\"id\":1004")))
.andExpect(content().string(containsString("\"name\":\"Bob zip\"")))
.andExpect(content().string(
containsString(("Kimchi")) // 메뉴추가 테스트
));
mvc.perform(get("/restaurants/2020"))
.andExpect(status().isOk())
.andExpect(content().string(containsString("\"id\":2020")))
.andExpect(content().string(containsString("\"name\":\"Cyber Food\"")));
}
}
@Test
public void detail() throws Exception {
mvc.perform(get("/restaurants/1004"))
.andExpect(status().isOk())
.andExpect(content().string(containsString("\"id\":1004")))
.andExpect(content().string(containsString("\"name\":\"Bob zip\"")))
.andExpect(content().string(
containsString(("Kimchi")) // 메뉴추가 테스트
));
이렇게 "/restaurants/1004"요청시 이 부분이 정상적인 테스트가 되는지 확인할 것이다.
/main/~/interfaces/RestaurantController.java
package kr.co.fastcampus.eatgo.interfaces;
import kr.co.fastcampus.eatgo.domain.MenuItem;
import kr.co.fastcampus.eatgo.domain.MenuItemRepository;
import kr.co.fastcampus.eatgo.domain.Restaurant;
import kr.co.fastcampus.eatgo.domain.RestaurantRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class RestaurantController {
@Autowired // 객체를 따로 생성안해도 @Component가 붙어있는 클래스를 찾아 자동으로 의존주입을하게된다.
private RestaurantRepository restaurantRepository;
@Autowired
private MenuItemRepository menuItemsRepository; // MenuItemRepository 의존주입
@GetMapping("/restaurants") // 가게목록 반환 api
public List<Restaurant> list(){
List<Restaurant> restaurants = restaurantRepository.findAll();
return restaurants;
}
//-----------------------우리가 지금 테스트할 곳--------------------------------------
@GetMapping("/restaurants/{id}") // 특정 가게상세 반환 api
public Restaurant detail(@PathVariable("id") Long id){
Restaurant restaurant = restaurantRepository.findById(id);
List<MenuItem> menuItems = menuItemsRepository.findAllByRestaurantId(id); // 요청되는 id에 따른 가게 메뉴리스트 생성
restaurant.setMenuItems(menuItems); // 특정가게의 메뉴목록들 추가
return restaurant;
}
//-----------------------------------------------------------------------------------
}
@Autowired
private RestaurantRepository restaurantRepository;
이 라인에서 의존주입으로 자동객체 생성
아래에서 객체생성시 생성자 확인
/main/~/domain/RestaurantRepositoryImpl.java
package kr.co.fastcampus.eatgo.domain;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
@Component // 스프링이 관리할수 있게 @Component를 붙인다. 그럼 Autowired가 사용되는곳에서 이 @Component가 붙어있는곳을찾아 자동으로 의존성주입을 하게된다.
public class RestaurantRepositoryImpl implements RestaurantRepository {
private List<Restaurant> restaurants = new ArrayList<>(); // 가게목록 담을 리스트
public RestaurantRepositoryImpl(){ // 생성자를 이용해 가게 객체 추가
restaurants.add(new Restaurant(1004L,"Bob zip", "Seoul"));
restaurants.add(new Restaurant(2020L, "Cyber Food", "Seoul"));
}
@Override
public List<Restaurant> findAll() { // 가게목록 반환
return restaurants;
}
@Override
public Restaurant findById(Long id) { // 특정가게 상세 반환
return restaurants.stream()
.filter(r -> r.getId().equals(id))
.findFirst()
.orElse(null);
}
}
private List restaurants = new ArrayList<>();
public RestaurantRepositoryImpl(){
restaurants.add(new Restaurant(1004L,"Bob zip", "Seoul"));
restaurants.add(new Restaurant(2020L, "Cyber Food", "Seoul"));
}
생성자에서 가게 각각의 객체를 가게리스트에 추가해주는것을 확인할수있다.
@Override
public Restaurant findById(Long id) { // 특정가게 상세 반환return restaurants.stream() .filter(r -> r.getId().equals(id)) .findFirst() .orElse(null);
}
그리고 findById() 메서드에서 요청받은 id값에 맞는 객체를 반환해주는것을 확인할 수 있다.
/main/~/interfaces/RestaurantController.java
package kr.co.fastcampus.eatgo.interfaces;
import kr.co.fastcampus.eatgo.domain.MenuItem;
import kr.co.fastcampus.eatgo.domain.MenuItemRepository;
import kr.co.fastcampus.eatgo.domain.Restaurant;
import kr.co.fastcampus.eatgo.domain.RestaurantRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class RestaurantController {
@Autowired // 객체를 따로 생성안해도 @Component가 붙어있는 클래스를 찾아 자동으로 의존주입을하게된다.
private RestaurantRepository restaurantRepository;
@Autowired
private MenuItemRepository menuItemsRepository; // MenuItemRepository 의존주입
@GetMapping("/restaurants") // 가게목록 반환 api
public List<Restaurant> list(){
List<Restaurant> restaurants = restaurantRepository.findAll();
return restaurants;
}
//-----------------------우리가 지금 테스트할 곳--------------------------------------
@GetMapping("/restaurants/{id}") // 특정 가게상세 반환 api
public Restaurant detail(@PathVariable("id") Long id){
Restaurant restaurant = restaurantRepository.findById(id);
List<MenuItem> menuItems = menuItemsRepository.findAllByRestaurantId(id); // 요청되는 id에 따른 가게 메뉴리스트 생성
restaurant.setMenuItems(menuItems); // 특정가게의 메뉴목록들 추가
return restaurant;
}
//-----------------------------------------------------------------------------------
}
@Autowired
private MenuItemRepository menuItemsRepository;
이 라인에서 의존주입으로 자동객체 생성
아래에서 객체생성시 생성자 확인
/main/~/domain/MenuItemRepositoryImpl.java
package kr.co.fastcampus.eatgo.domain;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
@Component
public class MenuItemRepositoryImpl implements MenuItemRepository{
List<MenuItem> menuItems = new ArrayList<>(); // 메뉴들 담을 리스트 생성
MenuItemRepositoryImpl(){
menuItems.add(new MenuItem("Kimchi")); // 메뉴리스트에 메뉴추가
}
@Override
public List<MenuItem> findAllByRestaurantId(Long restaurantId) {
return menuItems; // 현재는 임시로 가게 id에 따른 메뉴리스트반환이 아닌 무조건적인 추가된 메뉴리스트 반환으로 설정해놨음
}
}
/main/~/domain/MenuItem.java
package kr.co.fastcampus.eatgo.domain;
public class MenuItem {
private final String name;
public MenuItem(String name) { // 생성자
this.name = name;
}
public String getName(){
return name;
}
}
/main/~/interfaces/RestaurantController.java
package kr.co.fastcampus.eatgo.interfaces;
import kr.co.fastcampus.eatgo.domain.MenuItem;
import kr.co.fastcampus.eatgo.domain.MenuItemRepository;
import kr.co.fastcampus.eatgo.domain.Restaurant;
import kr.co.fastcampus.eatgo.domain.RestaurantRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class RestaurantController {
@Autowired // 객체를 따로 생성안해도 @Component가 붙어있는 클래스를 찾아 자동으로 의존주입을하게된다.
private RestaurantRepository restaurantRepository;
@Autowired
private MenuItemRepository menuItemsRepository; // MenuItemRepository 의존주입
@GetMapping("/restaurants") // 가게목록 반환 api
public List<Restaurant> list(){
List<Restaurant> restaurants = restaurantRepository.findAll();
return restaurants;
}
//-----------------------우리가 지금 테스트할 곳--------------------------------------
@GetMapping("/restaurants/{id}") // 특정 가게상세 반환 api
public Restaurant detail(@PathVariable("id") Long id){
Restaurant restaurant = restaurantRepository.findById(id);
List<MenuItem> menuItems = menuItemsRepository.findAllByRestaurantId(id); // 요청되는 id에 따른 가게 메뉴리스트 생성
restaurant.setMenuItems(menuItems); // 특정가게의 메뉴목록들 추가
return restaurant;
}
//-----------------------------------------------------------------------------------
}
위 과정들에서 menuItems.add(new MenuItem("Kimchi"));로 menuItems 가 반환되어 있는 상태인것을 확인할 수 있다.
이 리스트가 아래의 코드에서 setMenuItems(menuItems)파라미터에 들어간다.
restaurant.setMenuItems(menuItems);
/main/~/domain/Restaurant.java
package kr.co.fastcampus.eatgo.domain;
import jdk.internal.jimage.ImageStrings;
import java.util.ArrayList;
import java.util.List;
public class Restaurant {
private final Long id;
private final String name;
private final String address;
private List<MenuItem> menuItems = new ArrayList<MenuItem>(); // 가게메뉴들을 담을 리스트 생성
public Restaurant(Long id, String name, String address) {
this.id = id;
this.name = name;
this.address = address;
}
public Long getId() {
return id;
}
public String getName() { // 생성된 객체의 이름 반환
return name;
}
public String getAddress() { // 생성된 객체의 주소 반환
return address;
}
public String getInformaion() { // 생성된 객체의 정보 반환
return name + " in " + address;
}
public List<MenuItem> getMenuItems() { // 가게의 메뉴들 반환
return menuItems;
}
public void addMenuItem(MenuItem menuItem) { // 2. 아래 메서드에서 하나씩 뽑힌 객체가 menuItems에 추가 된다.
menuItems.add(menuItem);
}
public void setMenuItems(List<MenuItem> menuItems) { // 1. 리스트에 담긴 메뉴를 for문으로 뽑아 addMenuItme(menuItem)에 객체 하나씩으로 넣는다.
for (MenuItem menuItem : menuItems){
addMenuItem(menuItem);
}
}
}
그리하여 "http://localhost:8080/restaurants/1004" 의 요청결과는 아래와같이 나오게된다.
{"id":1004,"name":"Bob zip","address":"Seoul","menuItems":[{"name":"Kimchi"}],"informaion":"Bob zip in Seoul"}