TIL/JDBC

[JDBC] Controller, JUnit

yndev 2022. 2. 8. 09:26

이전 게시글에서 해온 작업 방식이 OrderMenu라고 하는 View에 해당 하는 것이

OrderService라고 하는 Service 레이어를 호출하는 구조였는데,

중간에 Controller라는 클래스를 거쳐서 동작하도록 끼워넣는 작업을 할 것이다.

OrderService로 메소드를 불러준 곳을 OrderController로 바꿔주고 OrderController클래스에 메소드를 추가해준다.

 

마찬가지로 registOrder메소드도 OrderController로 바꿔주는데 위에 드래그한 값들은 데이터에 대한 가공처리를 한 부분은 제거해주고, 리턴값에 대한 if문도 지워준다.

  =>수정된 코드

public class OrderMenu {
	
	private OrderController orderController = new OrderController();

	public void displayMenu() {
		
		Scanner sc = new Scanner(System.in);
		
		List<OrderMenuDTO> orderMenuList = new ArrayList<>();
		int totalOrderPrice = 0;
		
		do {
			System.out.println("========== 음식 주문 프로그램 ==========");
			
			List<CategoryDTO> categoryList = orderController.selectAllCategory();
			
			for(CategoryDTO category : categoryList) {
				System.out.println(category.getName());
			}
			
			System.out.println("====================================");
			System.out.print("주문하실 카테고리를 선택해주세요 : ");
			String inputCategory = sc.nextLine();
			
			int categoryCode = 0;
			for(CategoryDTO category : categoryList) {
				if(category.getName().equals(inputCategory)) {
					categoryCode = category.getCode();
				}
			}
			System.out.println("========== 주문 가능 메뉴 ==========");
			List<MenuDTO> menuList = orderController.selectMenuByCategory(categoryCode);
			for(MenuDTO menu : menuList) {
				System.out.println(menu);
			}
			
			System.out.print("주문하실 메뉴를 선택해주세요 : ");
			String inputMenu = sc.nextLine();
			
			int menuCode = 0;
			int menuPrice = 0;
			
			for(int i = 0; i < menuList.size(); i++) {
				MenuDTO menu = menuList.get(i);
				if(menu.getName().equals(inputMenu)) {
					menuCode = menu.getCode();
					menuPrice = menu.getPrice();
				}
			}
			
			System.out.print("주문하실 수량을 입력하세요 : ");
			int orderAmount = sc.nextInt();
			
			OrderMenuDTO orderMenu = new OrderMenuDTO();
			orderMenu.setMenuCode(menuCode);
			orderMenu.setOrderAmount(orderAmount);
			
			orderMenuList.add(orderMenu);
			
			totalOrderPrice += (menuPrice * orderAmount);
			
			System.out.print("계속 주문하시겠습니까? (예/아니오) : ");
			sc.nextLine();
			boolean isContinue = sc.nextLine().equals("예") ? true : false;
			
			if(!isContinue) break;
			
		} while(true);
		
		for(OrderMenuDTO orderMenu : orderMenuList) {
			System.out.println(orderMenu);
		}
		//금액과 메뉴를 하나의 맵에 같이 저장한다. 
		Map<String, Object> requestMap = new HashMap<>();
		requestMap.put("totalOrderPrice", totalOrderPrice);
		requestMap.put("orderMenuList", orderMenuList);
		
		orderController.registOrder(requestMap);
		

	}

}

 

Controller의 역할

뷰에서 사용자가 입력한 정보를 파라미터 형태로 전달 받으면

전달 받은 값들을 검증하거나 추가적인 정보가 필요한 경우 가공을 한 뒤

서비스 쪽으로 전달하기 위한 인스턴스에 담고 서비스의 비즈니스 로직을 담당하는 메소드를 호출한다.
또한 호출한 수행 결과를 반환 받아 어떠한 뷰를 다시 사용자에게 보여줄 것인지를 결정하는 역할을 한다.

 

OrderController 클래스

selectAllCategory, selectMenuByCategory는 딱히 검증할 게 없어서 Service에 있는 것들을 호출하기만 했다.

public class OrderController {

	private OrderService orderService = new OrderService();
	
	public List<CategoryDTO> selectAllCategory() {
	
		List<CategoryDTO> categoryList = orderService.selectAllCategory();
		return categoryList;
	}

	public List<MenuDTO> selectMenuByCategory(int categoryCode) {
		
		return orderService.selectMenuByCategory(categoryCode);
		
	}

	public void registOrder(Map<String, Object> requestMap) {
		//1. 뷰에서 전달 받은 파라미터 꺼내서 변수에 담기
		int totalOrderPrice = (Integer)requestMap.get("totalOrderPrice");
		List<OrderMenuDTO> orderMenuList = (List<OrderMenuDTO>)requestMap.get("orderMenuList");
		
		//2. 추가적으로 필요한 값이 있는 경우 생성하기
		java.util.Date orderTime = new java.util.Date();
		SimpleDateFormat dateFormat = new SimpleDateFormat("yy/MM/dd");
		SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss");
		String date = dateFormat.format(orderTime);
		String time = timeFormat.format(orderTime);
		
		//3. 서비스쪽으로 전달하기 위해 DTO 인스턴스에 담기 
		OrderDTO order = new OrderDTO();
		order.setDate(date);
		order.setTime(time);
		order.setTotalOrdderPrice(totalOrderPrice);
		order.setOrderMenuList(orderMenuList);
		
		//4. 서비스(비즈니스 로직)를 호출하고 결과를 리턴 받음
		int result = orderService.registOrder(order);
		
		//5. 서비스 처리결과를 이용해 성공 여부를 판단하여 사용자에게 보여줄 뷰를 결정함
		ResultView resultView = new ResultView();
		if(result > 0) {
			resultView.success();
		} else {
			resultView.failed();
		}
	}

}
public class ResultView {

	public void success() {
		System.out.println("주문에 성공하셨습니다.");
	}
	
	public void failed() {
		System.out.println("주문에 실패하셨습니다.");
	}
	
}

테스트 시나리오

public class Calculator {
	
	public int sumTwoNumber(int first, int second) {
		return first + second;
	}

}

 

1. Calculator 인스턴스 생성이 잘 되는지 테스트

2. sumTwoNumber메소드가 정상 기능하는지 테스트

3. 위 테스트 결과가 모두 통과되면 해당 클래스의 메소드는 신뢰성 있는 메소드임을 확인

public class Application {

	public static void main(String[] args) {
		
		Calculator calc = new Calculator();
		
		if(calc != null) {
			System.out.println("성공");
		} else {
			System.out.println("실패");
		}
		
		//2. sumTwoNumber메소드가 정상 기능하는지 테스트
		//2-1. 4와 5를 전달하면 합계 9가 계산 되는지 확인
		int result1 = calc.sumTwoNumber(4, 5);
		
		if(result1 == 9) {
			System.out.println("4와 5를 전달하여 합계가 9인지 확인");
		} else {
			System.out.println("4와 5를 전달하여 합계가 9가 아님");
		}
		
		//2-2. 6과 7을 전달하면 합계가 13이 되는지 확인
		int result2 = calc.sumTwoNumber(6, 7);
		
		if(result2 == 13) {
			System.out.println("6과 7을 전달하여 합계가 13인지 확인");
		} else {
			System.out.println("6과 7을 전달하여 합계가 13이 아님");
		}
		
		//3. 위 테스트 결과가 모두 통과되면 해당 클래스의 메소드는 신뢰성 있는 메소드임을 확인
		if(result1 == 9 && result2 == 13) {
			System.out.println("테스트 성공");
		} else {
			System.out.println("테스트 실패");
		}
	}
}

 

위의 간단한 테스트를 Junit으로 바꿔보자.

 

단위 테스트(Unit Test)
- 한 가지 기능(함수)마다 일을 잘 수행하는지 확인하며 특정 모듈이 의도된 대로 정확히 작동하는지 검증하는 절차
- 연관 컴포넌트가 개발되지 않더라도 기능별 개발이 완료된 것을 증명할 수 있음

 

@Before

- @Test가 작성된 메소드 호출 이전에 반복되는 준비 작업을 위한 메소드에 작성

- 테스트 메소드를 실행하기 전에 먼저 자동으로 실행

 

@After

- @Test가 작성된 메소드 호출 이후 실행

 

위에서 작성한 Calculator는 그대로 복사해오고,

CalculatorTests 클래스를 작성해준다.

public class CalculatorTests {

	private Calculator calc = null;
	
	@Before
	public void setup() {
		System.out.println("calculator 인스턴스 생성");
		calc = new Calculator();
	}
	
	@Test
	public void testSumTwoNumber_4와_5를_전달하면_합계가_9가_계산되는지_확인() {
		System.out.println("2-1 테스트 동작");
		int result = calc.sumTwoNumber(4, 5);
		
		assertEquals(9, result);
	}
	
}

 

작성을 하면서 @Before 어노테이션을 달아주기 전에 

해당 클래스 Properties-java build path-Libraries에서 Add Library를 선택해준다.

 

거기서 JUnit 추가

 

이후 다시 와서 @Before org.junit을 import할 수 있다.

 

이후에 실행을 해주면 JUnit창이 새로 뜨고 문제가 없다면 메소드 수행결과가 잘 됐는지 결과가 나온다.

 

assertEquals에 일부러 답을 틀리게 적으면 에러가 왜 틀렸는지 Failur Trace에 나온다.

 

마지막 메소드를 보면 6과 7을 전달하면 합계가 13인지 확인하는 메소드이다.

assertEquals(12, result, 1); 의 뜻은 1까지의 오차는 허용을 한다는 뜻이다.

이외 assert.. 함수는

 

assertArrayEqauls(a, b) : 배열 a와 b가 일치함을 확인

assertEquals(a, b) : 객체 a와 b의 값이 같은지 확인

assertSam(a, b) : 객체 a와 b가 같은 객체임을 확인

asertTrue(a) : a가 참인지 확인

assertNotNull(a) : a 객체가 null이 아님을 확인

 

@Ignore

- 테스트하고싶지 않을 경우 무시하도록 작성한다.

 

이전에 작성했던 OrderDAO로 테스트해보자.

package com.greedy.section03.layertests.model.dao;

import static com.greedy.common.JDBCTemplate.getConnection;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;

import java.sql.Connection;
import java.util.List;

import org.junit.Before;
import org.junit.Test;

import com.greedy.section03.layertests.model.dto.CategoryDTO;
import com.greedy.section03.layertests.model.dto.OrderDTO;;
public class OrderDAOTests {

	private Connection con;
	private OrderDAO orderDAO;
	private OrderDTO order;
	
	@Before
	public void setup() {
		con = getConnection();
		orderDAO = new OrderDAO();
		
		order = new OrderDTO();
		order.setDate("22/02/07");
		order.setTime("12:44:55");
		order.setTotalOrdderPrice(30000);
	}
	
	@Test
	public void testSelectAllCateogory() {
		
		List<CategoryDTO> categoryList = orderDAO.selectAllCategory(con);
		
		assertNotNull(categoryList);
	}
	
	@Test
	public void testInsertOrder() {
		
		int result = orderDAO.insertOrder(con, order);
		
		assertEquals(1, result);
	}
}