[내일배움캠프 Spring 4기] 52일차 TIL - Service Test | Controller Test

서예진·2024년 2월 22일
0

오늘의 학습 키워드💻

▸ 오늘의 코드카타
▸ Service Test
▸ Controller Test


▼ 오늘의 코드카타

2024년 2월 22일 - [프로그래머스] 28 : 행렬의 곱셈


✅ Service Test

  • TodoService를 테스트 했다.

TodoServiceTest

1. postTodo() 테스트

  • 해당 메서드를 수행하기 위해서는 TodoRequestDto 와 UserDetailsImpl을 전달해줘야한다.

▸ 문제

  • UserDetilsImpl에 대해서 테스트에서 어떻게 처리해야할지 고민이 됐다.

▸ 시행

  • 우선, @Mock를 통해 가짜 객체를 만들어서 테스트 코드를 작성해봤다.

▸ 결과

  • 테스트를 통과하긴 했다.

▸ 해결 및 알게된 점

  • @Mock을 활용하여 UserDetailsImpl을 가짜 객체로 선언해서 처리하면된다.
  • 이와 같이 테스트에 필요한 객체들은 @Mock를 통해 가짜 객체를 만들어서 테스트에서 사용한다.

TodoServiceTest

@ExtendWith(MockitoExtension.class) //@Mock 사용을 위해 설정
public class TodoServiceTest {

    @Mock
    TodoRepository todoRepository;

    @Mock
    CommentService commentService;

    @Mock
    UserDetailsImpl userDetails;
    @Test
    @DisplayName("postTodo메서드 - 할일카드 등록 성공")
    void test1() {
        //given
        String title = "제목";
        String content = "내용";

        TodoRequestDto todoRequestDto = new TodoRequestDto();
        todoRequestDto.setTitle(title);
        todoRequestDto.setContent(content);

        TodoService todoService = new TodoService(todoRepository, commentService);

        //when
        TodoResponseDto result = todoService.postTodo(todoRequestDto, userDetails);

        //then
        assertEquals(todoRequestDto.getTitle(), result.getTitle());
        assertEquals(todoRequestDto.getContent(), result.getContent());
    }
}
  • 실패 케이스에 대해서도 테스트 추가했다.
@Test
    @DisplayName("postTodo 메서드 - 할일카드 등록 실패")
    void test2() {
        //given
        String title = null;
        String content = "내용";

        TodoRequestDto todoRequestDto = new TodoRequestDto();
        todoRequestDto.setTitle(title);
        todoRequestDto.setContent(content);

        TodoService todoService = new TodoService(todoRepository, commentService);

        //when
        Exception exception = assertThrows(MethodArgumentNotValidException.class, () -> {
            todoService.postTodo(todoRequestDto, userDetails);
        });

        //then
        assertSame("제목을 입력하세요.", exception.getMessage());
    }

▸ 문제

  • 다음과 같은 오류가 발생했다.
  • 이 문제는 테스트 케이스가 MethodArgumentNotValidException이 발생할 것으로 예상하지만, 테스트 실행 중에 해당 예외가 발생하지 않는 것으로 보였다.

▸ 실행

  • 우선, postman에서 어떤 오류가 발생하는지 확인했다.
  • postman으로 확인했을 때 MethodArgumentNotValidException가 발생하는 건 맞았다.
  • 혹시 유효성 검증에 대한 오류이기 때문에 해당 오류는 Controller 단에서 발생하는 건 아닐까 싶어 TodoController 테스트에서 이 테스트 코드를 추가하기로 하고 이 테스트 코드는 삭제했다.

▸ 해결

  • Controller 테스트 부분에서 MethodArgumentNotValidException 예외에 대해서 테스트 했다.
  • Service 테스트 에서는 할 수 없는지 더 공부해야 할 것 같다.

2. getTodoByTodoId() 테스트

▸ 문제

@Test
    @DisplayName("할일카드 조회 성공")
    void test2() {
        //given
        Long id = 1L;
        String title = "제목";
        String content = "내용";

        TodoRequestDto todoRequestDto = new TodoRequestDto();
        todoRequestDto.setTitle(title);
        todoRequestDto.setContent(content);

        Todo todo = new Todo(todoRequestDto, userDetails);

        TodoService todoService = new TodoService(todoRepository, commentService);
        given(todoRepository.findById(id)).willReturn(Optional.of(todo));

        //when
        TodoResponseDto result = todoService.getTodoByTodoId(id);

        //then
        assertEquals(1L,result.getId());
        assertEquals("제목", result.getTitle());
        assertEquals("내용", result.getContent());

    }
  • 위와 같이 작성했지만, 아래와 같이 테스트에 실패했다.
  • 지금 Todo 객체에 id를 set해주지 않기 때문에 오류가 발생한 것 같다.

▸ 실행

  • 다시 생각해보니 위의 메서드 테스트에서는 할일카드를 조회하는 기능만 테스트 하기 때문에 assertEquals가 아니라 assertNotNull을 적용했다.
  • 즉, 실제 데이터베이스에 저장되었는지는 고려하지 않아도 된다.

▸ 해결

TodoServiceTest

@Test
    @DisplayName("할일카드 조회 성공")
    void test2() {
        //given
        Long id = 1L;
        String title = "제목";
        String content = "내용";

        TodoRequestDto todoRequestDto = new TodoRequestDto();
        todoRequestDto.setTitle(title);
        todoRequestDto.setContent(content);

        Todo todo = new Todo(todoRequestDto, userDetails);

        TodoService todoService = new TodoService(todoRepository, commentService);
        given(todoRepository.findById(id)).willReturn(Optional.of(todo));

        //when
        TodoResponseDto result = todoService.getTodoByTodoId(id);

        //then
        assertNotNull(result);
    }

TodoServiceTest

  • "할일카드 조회 성공" 테스트 코드를 활용해서 할일카드 조회 실패 케이스에 대해 테스트 했다.
@Test
    @DisplayName("할일카드 조회 실패")
    void Test3(){
        //given
        Long id = 1L;

        TodoService todoService = new TodoService(todoRepository, commentService);

        //when
        IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, ()-> {
            todoService.getTodoByTodoId(id);
        });

        //then
        assertEquals("존재하지 않는 할일 id 입니다.", exception.getMessage());
    }

3. updateTodo() 테스트

@Test
    @DisplayName("할일카드 수정 성공")
    void Test4() {
        //given
        Long id = 100L;
        String title = "제목";
        String content = "내용";
        String username = "yejin";
        TodoRequestDto requestDto = new TodoRequestDto();
        requestDto.setTitle(title);
        requestDto.setContent(content);

        Todo todo = new Todo(requestDto, userDetails);
        todo.setUsername(username);

        TodoService todoService = new TodoService(todoRepository, commentService);

        given(todoRepository.findById(id)).willReturn(Optional.of(todo));
        given(userDetails.getUsername()).willReturn(username);


        String title1 = "제목1";
        String content1 = "내용1";
        TodoRequestDto requestDto1 = new TodoRequestDto();
        requestDto1.setTitle(title1);
        requestDto1.setContent(content1);

        //when
        TodoResponseDto result = todoService.updateTodo(id, requestDto1, userDetails);

        //then
        assertEquals(title1, result.getTitle());
        assertEquals(content1, result.getContent());

    }

@Test
    @DisplayName("할일카드 수정 실패")
    void Test5() {
        //given
        Long id = 100L;
        String title = "제목";
        String content = "내용";
        String username = "yejin";
        TodoRequestDto requestDto = new TodoRequestDto();
        requestDto.setTitle(title);
        requestDto.setContent(content);

        Todo todo = new Todo(requestDto, userDetails);
        todo.setUsername("yejinyejin");

        TodoService todoService = new TodoService(todoRepository, commentService);

        given(todoRepository.findById(id)).willReturn(Optional.of(todo));
        given(userDetails.getUsername()).willReturn(username);


        String title1 = "제목1";
        String content1 = "내용1";
        TodoRequestDto requestDto1 = new TodoRequestDto();
        requestDto1.setTitle(title1);
        requestDto1.setContent(content1);

        //when
        Exception exception = assertThrows(RejectedExecutionException.class, ()->{
            todoService.updateTodo(id, requestDto1, userDetails);
        });

        //then
        assertEquals("할일카드의 작성자만 수정이 가능합니다.", exception.getMessage());


    }


✅ Controller Test

MockSpringSecurityFilter

  • Spring Security가 테스트 하는데 방해를 하기 때문에 가짜 객체처럼 가짜 filter를 만든다.
  • 이 필터를 통해서 가짜 인증을 할 수 있다.
public class MockSpringSecurityFilter implements Filter{
    @Override
    public void init(FilterConfig filterConfig) {}

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        SecurityContextHolder.getContext()
                .setAuthentication((Authentication) ((HttpServletRequest) req).getUserPrincipal());
        chain.doFilter(req, res);
    }

    @Override
    public void destroy() {
        SecurityContextHolder.clearContext();
    }
}

UserTodoTest - 할일카드 등록 성공

@WebMvcTest(
        controllers = {TodoController.class},
        excludeFilters = {
                @ComponentScan.Filter(
                        type = FilterType.ASSIGNABLE_TYPE,
                        classes = WebSecurityConfig.class
                )
        }
)
public class UserTodoMvcTest {
    private MockMvc mvc;

    private Principal mockPrincipal;

    @Autowired
    private WebApplicationContext context;

    @Autowired
    private ObjectMapper objectMapper;

    @MockBean
    TodoService todoService;

    @MockBean
    UserService userService;

    @BeforeEach
    public void setup(){
        mvc = MockMvcBuilders.webAppContextSetup(context)
                .apply(springSecurity(new MockSpringSecurityFilter()))
                .build();
    }

    private void mockUserSetuo(){
        String username = "yejin";
        String password = "yejin1234";
        User testUser = new User(username, password);
        UserDetailsImpl userDetails = new UserDetailsImpl(testUser);
        mockPrincipal = new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
    }
    @Test
    @DisplayName("할일카드 등록 성공")
    void test1() throws Exception{
        //given
        this.mockUserSetuo();
        String title = "제목";
        String content = "내용";
        TodoRequestDto requestDto = new TodoRequestDto();
        requestDto.setTitle(title);
        requestDto.setContent(content);

        String postInfo = objectMapper.writeValueAsString(requestDto);

        //when - then
        mvc.perform(post("/api/todos")
                .content(postInfo)
                .contentType(MediaType.APPLICATION_JSON)
                .accept(MediaType.APPLICATION_JSON)
                .principal(mockPrincipal)
            )
            .andExpect(status().is3xxRedirection())
                .andExpect(view().name("redirect:/api/todos/myTodos"))
                .andDo(print());
    }
}
  • 찾아보니 테스트에 사용할 User와 UserDetailsImpl에 대해서 mockUserSetuo()와 같이 메서드를 만들어 주고 사용하면 될 것 같다.

UserTodoTest - 할일카드 등록 실패

  • Methodargumentnotvalidexception 예외에 대해 테스트 했다.
@Test
    @DisplayName("할일카드 등록 실패")
    void test2() throws Exception{
        //given
        this.mockUserSetuo();
        String title = "";
        String content = "내용";
        TodoRequestDto requestDto = new TodoRequestDto();
        requestDto.setTitle(title);
        requestDto.setContent(content);

        String postInfo = objectMapper.writeValueAsString(requestDto);

        //when - then
        mvc.perform(post("/api/todos")
                        .content(postInfo)
                        .contentType(MediaType.APPLICATION_JSON)
                        .accept(MediaType.APPLICATION_JSON)
                        .principal(mockPrincipal)
                )
                .andExpect(status().isBadRequest())
                .andExpect(MockMvcResultMatchers.jsonPath("$.errorMessage").value("제목을 입력하세요."))
                .andExpect(MockMvcResultMatchers.jsonPath("$.statusCode").value(400))
                .andDo(print());
    }
profile
안녕하세요

0개의 댓글