단위 테스트 코드 작성 및 실행

다음을 수행하여 단위 테스트 코드를 작성하고 테스트를 실행합니다.

  • STS에서 src/test/java 하위에 있는 io.infograb.order.controller 패키지(package)를 선택합니다.

  • File > New > Class를 선택한 다음, Name 필드에 OrderControllerTest을 입력하고 Finish 버튼을 클릭합니다.

  • OrderControllerTest.java 파일 내용을 아래와 같이 수정합니다.

    package io.infograb.order.controller;
    
    import static org.hamcrest.CoreMatchers.is;
    import static org.mockito.BDDMockito.given;
    import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
    import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
    
    import org.junit.jupiter.api.BeforeEach;
    import org.junit.jupiter.api.DisplayName;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
    import org.springframework.boot.test.mock.mockito.MockBean;
    import org.springframework.http.MediaType;
    import org.springframework.test.web.servlet.MockMvc;
    import org.springframework.test.web.servlet.ResultActions;
    import org.springframework.test.web.servlet.setup.MockMvcBuilders;
    import org.springframework.web.context.WebApplicationContext;
    import org.springframework.web.filter.CharacterEncodingFilter;
    
    import io.infograb.order.service.OrderService;
    
    @DisplayName("Test OrderController")
    @WebMvcTest(OrderController.class)
    public class OrderControllerTest {
    
        @Autowired
        private MockMvc mockMvc;
    
        @Autowired
        private WebApplicationContext context;
    
        @MockBean
        private OrderService orderService;
    
        @BeforeEach
        public void setUp() {
            this.mockMvc = MockMvcBuilders.webAppContextSetup(context)
                    .addFilters(new CharacterEncodingFilter("UTF-8", true))
                    .alwaysDo(print())
                    .build();
        }
    
        @DisplayName("Should order successfully")
        @Test
        public void shouldOrder() throws Exception {
            // given
            given(orderService.orderDrink("user01", 3)).willReturn(true);
    
            // when
            final ResultActions actions = this.mockMvc.perform(post("/orders?userId=user01&menuId=3")
                    .contentType(MediaType.APPLICATION_JSON));
    
            // then
            actions.andExpect(status().isOk())
                    .andExpect(jsonPath("$.message", is("주문이 완료되었습니다.")));
        }
    
        @DisplayName("Should return fail message when not enough balance")
        @Test
        public void shouldReturnFailMessageWhenNotEnoughBalance() throws Exception {
            // given
            given(orderService.orderDrink("user01", 3)).willReturn(true);
            given(orderService.orderDrink("user01", 3)).willReturn(false);
    
            // when
            final ResultActions actions = this.mockMvc.perform(post("/orders?userId=user01&menuId=3")
                    .contentType(MediaType.APPLICATION_JSON));
    
            // then
            actions.andExpect(status().isOk())
                    .andExpect(jsonPath("$.message", is("잔액이 부족합니다.")));
        }
    
    }
  • io.infograb.order.service 패키지에 OrderServiceTest.java 파일을 생성하고 아래와 같이 수정합니다.

    package io.infograb.order.service;
    
    import static org.junit.jupiter.api.Assertions.assertFalse;
    import static org.junit.jupiter.api.Assertions.assertTrue;
    import static org.mockito.BDDMockito.given;
    
    import org.junit.jupiter.api.DisplayName;
    import org.junit.jupiter.api.Test;
    import org.junit.jupiter.api.extension.ExtendWith;
    import org.mockito.InjectMocks;
    import org.mockito.Mock;
    import org.mockito.junit.jupiter.MockitoExtension;
    
    import io.infograb.order.mapper.MenuMapper;
    import io.infograb.order.mapper.UserMapper;
    import io.infograb.order.model.Menu;
    import io.infograb.order.model.User;
    
    @DisplayName("Test OrderService")
    @ExtendWith(MockitoExtension.class)
    public class OrderServiceTest {
    
        @InjectMocks
        private OrderService orderService;
    
        @Mock
        private UserMapper userMapper;
    
        @Mock
        private MenuMapper menuMapper;
    
        @DisplayName("Should return true if order successful")
        @Test
        public void shouldOrderSuccessful() throws Exception {
            // given
            User user = new User();
            user.setId("user01");
            user.setName("홍길동");
            user.setPhone("010-1234-5678");
            user.setEmail("gdhong@infograb.dev");
            user.setUseNickname(true);
            user.setNickname("신출귀몰");
            user.setBalance(10000);
            user.setRewards(0);
    
            Menu menu = new Menu();
            menu.setId(1);
            menu.setName("아이스 카페 아메리카노");
            menu.setEnglishName("Iced Caffe Americano");
            menu.setPrice(4100);
    
            given(userMapper.selectUserById("user01")).willReturn(user);
            given(menuMapper.selectMenuById(1)).willReturn(menu);
    
            // when
            boolean expectedResult = orderService.orderDrink("user01", 1);
    
            // then
            assertTrue(expectedResult);
        }
    
        @DisplayName("Should return false when not enough balance")
        @Test
        public void shouldBeFailedOrder() throws Exception {
            // given
            User user = new User();
            user.setId("user01");
            user.setName("홍길동");
            user.setPhone("010-1234-5678");
            user.setEmail("gdhong@infograb.dev");
            user.setUseNickname(true);
            user.setNickname("신출귀몰");
            user.setBalance(4000);
            user.setRewards(0);
    
            Menu menu = new Menu();
            menu.setId(3);
            menu.setName("아이스 카페 모카");
            menu.setEnglishName("Iced Caffe Mocha");
            menu.setPrice(5100);
    
            given(userMapper.selectUserById("user01")).willReturn(user);
            given(menuMapper.selectMenuById(3)).willReturn(menu);
    
            // when
            boolean expectedResult = orderService.orderDrink("user01", 3);
    
            // then
            assertFalse(expectedResult);
        }
    
    }
  • io.infograb.order.mapper 패키지에 UserMapperTest.java 파일을 생성하고 아래와 같이 수정합니다.

    package io.infograb.order.mapper;
    
    import static org.junit.jupiter.api.Assertions.assertEquals;
    import static org.junit.jupiter.api.Assertions.assertNotNull;
    import static org.junit.jupiter.api.Assertions.assertNull;
    import static org.junit.jupiter.api.Assertions.assertTrue;
    
    import org.junit.jupiter.api.DisplayName;
    import org.junit.jupiter.api.Test;
    import org.mybatis.spring.boot.test.autoconfigure.MybatisTest;
    import org.springframework.beans.factory.annotation.Autowired;
    
    import io.infograb.order.model.User;
    
    @DisplayName("Test UserMapperTest")
    @MybatisTest
    public class UserMapperTest {
    
        @Autowired
        private UserMapper userMapper;
    
        @DisplayName("Should return user")
        @Test
        public void shouldReturnUser() {
            User user = userMapper.selectUserById("user01");
            assertNotNull(user, "User was not found");
            assertEquals(user.getId(), "user01");
            assertEquals(user.getName(), "홍길동");
            assertEquals(user.getPhone(), "010-1234-5678");
            assertEquals(user.getEmail(), "gdhong@infograb.dev");
            assertTrue(user.isUseNickname());
            assertEquals(user.getNickname(), "신출귀몰");
        }
    
        @DisplayName("Should return Null if user ID does not exit")
        @Test
        void shouldUserNotFound() {
            User user = userMapper.selectUserById("mark85");
            assertNull(user, "User should not be found");
        }
    
    }
  • 터미널에서 mvn clean test 명령을 실행하여 단위 테스트 결과를 확인합니다.

    mvn clean test
    [INFO] Scanning for projects...
    [INFO]
    [INFO] -----------------< io.infograb.order:siren-order-api >------------------
    [INFO] Building Siren Order REST API 0.0.1-SNAPSHOT
    [INFO] --------------------------------[ jar ]---------------------------------
    [INFO]
    [INFO] --- maven-clean-plugin:3.1.0:clean (default-clean) @ siren-order-api ---
    [INFO] Deleting /Users/jason/Library/Mobile Documents/com~apple~CloudDocs/Workspace/siren-order-api/target
    [INFO]
    [INFO] --- maven-resources-plugin:3.2.0:resources (default-resources) @ siren-order-api ---
    [INFO] Using 'UTF-8' encoding to copy filtered resources.
    [INFO] Using 'UTF-8' encoding to copy filtered properties files.
    [INFO] Copying 1 resource
    [INFO] Copying 6 resources
    [INFO]
    [INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ siren-order-api ---
    [INFO] Changes detected - recompiling the module!
    [INFO] Compiling 9 source files to /Users/jason/Library/Mobile Documents/com~apple~CloudDocs/Workspace/siren-order-api/target/classes
    [INFO]
    [INFO] --- maven-resources-plugin:3.2.0:testResources (default-testResources) @ siren-order-api ---
    [INFO] Using 'UTF-8' encoding to copy filtered resources.
    [INFO] Using 'UTF-8' encoding to copy filtered properties files.
    [INFO] skip non existing resourceDirectory /Users/jason/Library/Mobile Documents/com~apple~CloudDocs/Workspace/siren-order-api/src/test/resources
    [INFO]
    [INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ siren-order-api ---
    [INFO] Changes detected - recompiling the module!
    [INFO] Compiling 6 source files to /Users/jason/Library/Mobile Documents/com~apple~CloudDocs/Workspace/siren-order-api/target/test-classes
    [INFO]
    [INFO] --- maven-surefire-plugin:2.22.2:test (default-test) @ siren-order-api ---
    [INFO]
    [INFO] -------------------------------------------------------
    [INFO]  T E S T S
    [INFO] -------------------------------------------------------
    
    (생략)
    
    [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.438 s - in io.infograb.order.controller.OrderControllerTest
    [INFO] Running io.infograb.order.service.OrderServiceTest
    [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.041 s - in io.infograb.order.service.OrderServiceTest
    [INFO] Running io.infograb.order.service.MenuServiceTest
    [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0 s - in io.infograb.order.service.MenuServiceTest
    2021-05-07 17:48:55.957  INFO 38829 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'
    2021-05-07 17:48:55.957  INFO 38829 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'
    [INFO]
    [INFO] Results:
    [INFO]
    [INFO] Tests run: 10, Failures: 0, Errors: 0, Skipped: 0
    [INFO]
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESS
    [INFO] ------------------------------------------------------------------------
    [INFO] Total time:  8.957 s
    [INFO] Finished at: 2021-05-07T17:48:56+09:00
    [INFO] ------------------------------------------------------------------------