Junit을 사용하다보니 현재 버전(Junit5)과 Junit4가 많아서 헷갈리는 점이 많았습니다. 오늘 포스팅에서는 직접 테스트해보면서 사용했던 코드와 차이 났던 부분에 대해서 정리해보았습니다. 또한 저는 Controller 부분을 테스트해보는 것이 중요하다고 생각했는데 사용자에게 요청을 받고, Model 객체를 통해 응답 하기 때문입니다.

@AutoConfigureMockMvc
@ExtendWith(SpringExtension.class)
@WebMvcTest(controllers = BookController.class)
class BookControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private BookService bookService;

    @MockBean
    private BookMapper bookMapper;

    @Autowired
    private ObjectMapper objectMapper;
    private Object val$mvcResult;

    @Test
    void createBook() throws Exception {
        BookDTO bookDTO = BookDTO.builder()
                .userId("test3")
                .id(20)
                .build();
        String bookDTOString = objectMapper.writeValueAsString(bookDTO);

        MvcResult result = mockMvc.perform(post("/book")
                        .param("userId", "test3")
                        .param("id", "20")
                        .content(bookDTOString)
                        .contentType(MediaType.APPLICATION_JSON))
                .andReturn();

        String content = result.getResponse().getContentAsString();

        ObjectMapper objectMapper = new ObjectMapper();
        BookDTO response = objectMapper.readValue(content, BookDTO.class);

        assertEquals("같습니다.", bookDTO, response);
    }
}

Junit 테스트에서는 실제 객체값을 사용할 수 없기 때문에 가짜 값인 Mock 객체를 사용합니다.

Mock 객체 사용시 장점은 아래와 같습니다.

  • 단언 실패시 실패한 테스트의 이름이 표기되어 빠르게 문제 파악 가능
  • 현재 실패한 테스트가 다른 테스트에 영향을 끼치지 않아 실패 테스트 디버깅에 필요한 시간을 단축.
  • 모든 케이스가 실행되었음을 보장. 단언실패시 이후의 테스트는 실행되지 않으므로 분리시켜야지 모든 케이스가 실행됨을 보장할수있다

BookDTO 값과 mock 객체를 만들어서 assetEquals로 서로 같은 값인지에 대해 비교하는 테스트를 진행하였습니다.

Runwith vs ExtendWith

Junit4까지는 Runwith(SpringRunner.class)을 사용했지만 junit5으로 넘어오면서 ExtendWith을 사용하게 되었습니다.

 

RunWith(SpringRunner.class)에 대해 정리해보자면

테스트 시 @RunWith(SpringRunner.class) -> SpringRunner.class -> SpringBootApplication이 지정된 클래스를 실행하게 되면서 결론적으로 스프링 컨테이너(.class)를 사용합니다.

(스프링 컨테이너란 빈을 관리해주는 공간이다.)

하지만 ExtendWith으로 변경 되면서 @ExtendWith 어노테이션은 @SpringBootTest를 포함으로 지정한 컨트롤러를 실행시켜 주는 역할을 하기 더 이상 @RunWith 어노테이션을 사용할 이유가 사라졌습니다.

 

assertThat

 

그 다음으로 Junit5로 넘어오면서 assert 메소드에도 변경사항이 생겼습니다 

 

Junit5에서 사용하는 assert 종류를 보면

  • assertArrayEquals  : 두 배열을 비교하여 일치 여부를 판단한다.
  • assertEquals  : 두 값을 비교하여 일치 여부를 판단한다
  • assertNull assertNotNull : 객체의 null 여부를 확인한다.
  • assertNotSame,assertSame : 두 변수가 동일한 객체를 참조하는지 확인한다.
  • assertTrue,assertFalse : 특정 조건이 true false인지 판단한다.
  • fail : 테스트를 실패하게 만들었을 때 사용한다.

여기서 Junit4에서 Junit5으로 넘어오게 되면서 빠지게 된 코드여도 assertThat을 사용하면 코드가 좀 더 깔끔해 보이는 것 같아보였습니다.

예를 들어 차례대로

assertEquals,assertTrue,assertFalse을 assertThat으로 사용 했을 때입니다.

1. assertEquals(“expected”, “actual”);
2. assertTrue(value);
3. assertFalse(value);

 

1. assertThat(actual, is(expected));
2. assertThat(actual, is(true));
3. assertThat(actual, is(false));

 

assertThat 정리되면서 실제 값과 기대 값의 차이를 확실히 구분할 수 있었기에 Junit5에서도 꼭 사용하고 싶어 어떻게 사용하나 봤더니 hamcrest 라이브러리를 추가하면 해결되는 것이였습니다.

import static org.hamcrest.MatcherAssert.assertThat;

assertEquals 등을 사용해보면서 assertThat보다 더 간결하게 코드를 작성할 수 있다고 느꼈습니다.

그외 @Before -> @BeforeEach, @After -> @Aftereach 등 더 바뀐 게 많습니다. 

 

하지만 Junit 테스트라는 것은 한계가 있습니다. 실제 DB를 타지 않고 본인들이 직접 값을 만들어 가짜로 비교를 하는 것이기 때문에 이 테스트의 정확도를 의심해 볼 필요가 있습니다. 그럼에도 불구하고 이 테스트를 진행하는 이유는 우리가 아예 테스트를 진행하지 않고 시장에 불확실한 프로그램을 내놓는것과 적어도 어느정도까지의 확실함을 가진 프로그램을 내놓는 것은 전혀 다를 것 입니다. 이번 Junit 테스트를 하면서 저도 정확도에 대해서 의구심을 떨칠 수 없었던 것은 아니지만 그렇기 때문에 더 정보를 찾아봤던 것 같습니다.

'PROJECT > 티켓팅 서버 프로젝트' 카테고리의 다른 글

Login Check 기능 AOP 적용  (0) 2022.11.03
Custom Exception 적용하기  (0) 2022.11.01
MVC 패턴 Controller 적용하기  (0) 2022.10.05
Jenkins CI/CD 적용하기  (0) 2022.09.20
locust 성능 테스트 시나리오  (0) 2022.08.29

+ Recent posts