(JPA+Boot)테스트코드 구현

자바 애플리케이션 개발에서 테스트 코드 작성 방법, 코드 커버리지 측정, 도메인/레포지토리/서비스/컨트롤러 계층별 테스트 전략, JUnit5와 AssertJ 활용법, Mock 객체 활용 등을 상세히 다루는 가이드입니다.



(Test) 테스트 코드 작성


코드 커버리지

코드 커버리지 내용이 궁금하다면 참고 링크

  • 인텔리J-edit configurations 세팅 및 테스트 실행 때 run test with 커버리지로 실행 ㄱ(run test with 커버리지 이것만해도 바로 됨)

    image

    • 참고로 edit configurations에서 Run 멀티 설정, 실행 프로필 설정 등 여러가지 할 수 있음.
  • 이클립스도 우클릭>Coverage as>Junit test 로 확인 가능


테스트 코드 작성

졸작은 JUnit5, 우테코는 AssertJ 사용했음, TDD는 테스트 주도 개발 방법론

보통 메모리DB로 테스트: application.properties 주석 시 boot-data-jpa 의존성이 이를 자동 제공 및 @Entity, @Id, @GeneratedValue로 JPA 어노테이션 사용해야 테이블 생성까지 자동..!

  • print대신 assert 로 비교하자
    • 예로 assertEquals함수 사용시 두개 인자가 “안 동일하면 오류, 동일하면 정상 동작”
  • (1) JUnit5

    • Assertions.assertNull, Assertions.assertInstanceOf, Assertions.assertEquals, Assertions.assertThrows 등등 자주 사용
    예시 참고 코드
    //어떤 객체인지 체크
    Assertions.assertInstanceOf(Character.class, character);
    //결과 비교 - equals
    Assertions.assertEquals(member, memberRepository.findOne(saveId));
    //예외 처리 + 예외 메시지까지 확인법
    Throwable exception = Assertions.assertThrows(IllegalStateException.class, () -> {
        followService.join(findFollow); // 중복검증 예외 발생
    });
    Assertions.assertEquals("이미 팔로우 요청을 하셨습니다.", exception.getMessage());
    log.info("exception.getMessage() : {}", exception.getMessage());
    //일부러 예외 터트린 테스트라면
    Assertions.fail("중복검증 예외가 발생해야 한다.");
    //프록시 체크 -> 서비스에 @Transactinal 은 프록시 사용
    Assertions.assertThat(AopUtils.isAopProxy(memberService)).isTrue();
    Assertions.assertThat(AopUtils.isAopProxy(memberRepository)).isFalse();
    
  • (2) AssertJ -> Junit5에서 파생된것

    • import static org.assertj.core.api.Assertions.*;
      assertThat(connection).isNotNull();
      assertThat(findMember).isEqualTo(member);
      assertThatThrownBy(() -> repository.findById(member.getMemberId()))
          .isInstanceOf(NoSuchElementException.class); // 예외 터지는거 확인 
      


테스트 코드 작성 TIP:

  • 예외 테스트:

    • (1)try, catch보다 간편하게 assertThrows를 사용해서 일부러 예외를 터트려서 테스트

    • (2)try, catch대신 @Test(expected = IllegalStateException.class) 를 선언하면 알아서 해당 예외 터질 때 종료해줌

      • 만약 해당 예외가 안터지면 그다음 코드들이 계속 실행됨. 그 코드는 아래 형태로 작성
      • Assertions.fail("예외가 발생해야 한다."); 예외가 안터져서 오히려 에러 라고 로그를 남겨줌
  • 스프링 테스트 선언법 (부트기준)

    • Junit4 : @RunWith, @SpringBootTest 둘다 선언 - 클래스 단에
    • Junit5 : @SpringBootTest 선언 - 클래스 단에 (@RunWith를 자동 제공)
    • @Test 를 메소드마다 선언하여 테스트!! - 메소드 단에
      • @RunWith(SpringRunner.class) : 스프링과 테스트 통합 -> Junit4 이하만 사용
      • @SpringBootTest : 스프링 컨테이너와 테스트를 통합 실행. @SpringBootApplication을 찾아서 모든 빈 로드. (이게 없으면 @Autowired 다 실패)
        • 레포, 서비스 때 주로 사용하게 됨.
        • 컨트롤러만 테스트 방법은 @WebMvcTest 사용 (서블릿 컨테이너 MOCK)
      • @SpringBootTest 의 빈 로드를 더 알아보자면,
        • 수동 빈 등록(ex:XML)할 때는 메인의 XML파일을 test/resources 하위로 복제하자!
          • xml수동 빈 등록은 메인에서 @ImportResource(“classpath:..)”로 직접 가져오는데,
            테스트환경에서는 classpath 가 test/resources 경로가 우선이라 못 가져온다.
            못 찾으면 main/resources 경로에서 찾는다고는 하던데 본인 경험상 못 찾더라.
        • 차라리 @TestConfiguration 테스트에서 스프링 빈 등록을 지원하는 어노테이션을 사용하는것도 있다.
  • given, when, then 예시 -> tdd 로 자동완성 사용 중

    • given에 멤버 이름 설정
    • when에 서비스의 join함수 사용(회원가입 되는지 확인하는 것)
    • then에 결과를 보는것. 멤버이름이 잘 생겼는지 등등..(assert보통 씀!)
  • 데이터 Init: @PostConstruct나 @BeforEach 등 활용

    • @BeforEach, @AfterEach는 각 @Test 수행 전, 후 동작

      • @Test마다 독립적 실행 상태라 필요 (@Test마다 실행해 줌)
      • 공통으로 테스트할 객체 생성하기 좋다.
      • 다만, DB와 연동하는 레포(DAO)단에선 @Transactional의 @Rollback(false)를 통해 DB의 데이터 공유는 전역으로 가능해서 테스트가 정상 동작 했던 것!
    • InitDB.java 로 개발 모드에서 간편하게 시작과 동시에 데이터를 미리 넣어두려는 목적(테스트 용이)

      InitDB.java 예시 코드:


      실행흐름: InitService 라는 서비스를 inner class 로 간단히 추가 및 빈 등록하고, @PostConstruct 로 바로 실행

          //안 사용할 거면 스프링 빈 등록 안되게끔(스캔X) @Component 주석 + @PostConstruct 주석
          @Slf4j
          @Component
          @RequiredArgsConstructor // 생성자 주입
          public class InitDB {
              private final InitService initService;
          //
              // 해당 클래스 인스턴스 생성(construct)된 후 자동 실행
              @PostConstruct
              public void init() {
                  initService.initItem();
              }
          //
              @Service
              @RequiredArgsConstructor
              @Transactional // 쓰기모드 -> 바로 DB 저장
              public static class InitService {
                  private final EntityManager em;
                  private final ItemRepository itemRepository;
                  public void initItem() {
                      Item item1 = Item.createItem("김익명", "123", "최근에 있었던 대외비", "최근에 이름 들으면 알 만한 회사랑 어떤 프로젝트 협업할 뻔했는데, 중간에 갑자기 뭐가 바껴서 결국 나랑 하기로 한 건 무산되었다. 너무 아까운 일이었는데 대외비라 어디에 이름 까고 말도 못해서 답답했음. 근데 최근에 보니까 그 프로젝트 초대박났더라고 하......^^^^;;; 또 생각해도 개빡치네*발ㅠㅠ", "test.jpeg");
                      Item item2 = Item.createItem("", "1234", "", "", "");
                      Item item3 = Item.createItem("철수", "123", "", "테스트", "test.jpeg");
                      itemRepository.save(item1);
                      itemRepository.save(item2);
                      itemRepository.save(item3);
                      List<Item> items = itemRepository.updateAllNo();
          //            for(int i= 0 ; i<150; i++) {
          //                String name = "test"+i;
          //                Item item = Item.createItem(name, "12345", "테스트", "테스트123123", "");
          //                itemRepository.save(item);
          //            }
                  }
              }
          }
      
  • @Transactional : 테스트를 실행할 때 마다 트랜잭션을 시작하고 테스트가 끝나면 트랜잭션을 강제로 롤백 (이 어노테이션은 테스트 케이스에서 사용될때만 기본값으로 롤백)

    • 롤백을 하기때문에 내부에서 굳이 영속성 컨텍스트 플러시를 안하는 특징을 가짐
    • @Rollback(false) : 롤백 취소
      • 롤백을 안하니까 자동flush 진행하므로 insert문 로그 확인가능
    • em.flush() 함수 사용 : flush 진행
      • 롤백은 건드리지않고 수동flush 진행하므로, 롤백은 그대로 진행하면서 insert문 로그 확인가능
    • 서비스 TDD 때 트랜잭션 선언안하면 서비스가 동작을 안하기 때문에 그냥 전역으로 써주자! 단,서비스 코드에 트랜잭션 있는건 꼭 확인
      • TDD에 선언한 트랜잭션때문에 서비스에 트랜잭션이 없어도 동작하기 때문!
      • 참고) 트랜잭션이 겹칠텐데 전파로 인해 기존 사용중인 트랜잭션을 그대로 사용하므로 문제가 없다.
  • @TestMethodOrder(MethodOrderer.OrderAnnotation.class)

    • @Order()+@Rollback(value=false) 활용 -> 주로 db저장 먼저하려고!
    • @Order(1), @Order(2) … 로 테스트 코드 실행 순서 지정 가능
      • 주의 : Order 라는 클래스가 이미 import 중이라면 @org.junit.jupiter.api.Order(1)
      • 또한, org.junit.jupiter.api 패키지의 Order 를 사용한다는 점을 기억
    • @DisplayName 는 간단히 테스트 출력때 항목 이름을 설정해서 출력 가능
  • @Slf4j: private static final Logger log = LoggerFactory.getLogger(YourClassName.class);와 같은 로깅 필드가 자동 생성

    • 덕분에 log.info("{}", member.getId()); 바로 사용
  • 컨트롤러”만” 테스트: @WebMvcTest 사용 (서블릿 컨테이너 MOCK)
    -> WAS 역할을 톰캣 대신 해주는것!

    • @MockBean 으로 가짜 객체 등록. -> 중요: 실제 동작X

      • 따라서 when(member.getId()).thenReturn(1L); 이런식으로 임의로 리턴값 지정
      • mockMvc.perform() 사용때 post, get 잘 구분해서 적용
      • Member member = mock(Member.class); 처럼 함수내에서 Mocking 객체도 간단 (@MockBean이랑 동작은 비슷할 듯)
      • 진짜 컨트롤러 코드만 테스트 하는것!
    • MockMvc의 목적은 3가지

      • request 잘 받는지 - content()
      • 서비스 메서드로 데이터 잘 전달 되는지 - verify()
        • times() 도 같이 많이 사용 (몇번 서비스 호출 되었나 볼 수 있음)
        • 왜냐하면 여러번 서비스 불리는 경우 몇번 불리는지 times로 명시해줘야 TooMany 에러 방지
      • 서비스 반환값으로 response 잘 받는지 - when, given 필수!
        • when, given 으로 해당 서비스의 반환값을 직접 설정가능!
        • response(응답) 관련 검증 메소드는 매우 다양!
          • andExpect() - status(), view(), redirectedUrl(), model(), content(), jsonPath() 등등.. 응답 관련 메소드..
          • andDo() : 요청, 응답관련 전체 메시지 출력
    • 로그인 세션 같이 세션 테스트는??

      • MockHttpSession() 활용 -> 가짜 세션으로 로그인 인증 “인터셉터” 통과

      • 반대로 세션을 반환하는 “로그인” 컨트롤러 테스트 경우?

        • 로그인 로직은 로그인 성공시 세션을 생성! (request 역할)

          image

        • @WebMvcTest 테스트 수행한 로그 request쪽에 session atr 보면 생성된 HttpServletRequest 정보 확인 가능

        • 다만, 응답 쿠키는 확인이 불가능했다. 쿠키는 웹에서는 자동 처리해주다보니 환경 차이인 것 같다.

    • 아래는 참고 메서드

      Mock관련 메서드들 참고
      1. perfom()
        • HTTP 요청을 할 수 있다.
        • 체이닝이 지원되어 여러 검증 기능을 이어서 선언할 수 있다.
      2. andExcept()
        • mvc.perform의 결과를 검증한다.
        • status() : 상태 코드를 검증할 수 있다.
        • view() : 리턴하는 뷰에 대해 검증한다.
          • ex) andExcept(view().name(“/detailpage”))
        • redirectedUrl() : 리다이렉트 url을 검증한다.
          • ex) redirectedUrl(“/where”);
        • model() : 컨트롤러의 Model에 관해 검증한다.
          • ex) model().attribute(“alcoholDetails”, alcoholDetails)
          • ex) model().attributeHasFieldErrorCode(“loginForm”, “userId”, “NotBlank”)
        • content() : 응답에 대한 정보를 검증한다.
        • jsonPath() : response body에 들어갈 json 데이터를 검증한다.
          • ex) jsonPath(“$.status”).value(“400”)
      3. andDo()
        요청/응답 전체 메세지를 확인할 수 있다.
      4. 추가 참고
        • Mock() - 모의 객체를 생성하는 역할
        • when() - 협력객체 메소드 반환 값을 지정해주는 역할(stub)
        • verify() - stub안의 협력객체 메소드가 호출 되었는지 확인
        • times() - 지정한 횟수 만큼 협력 객체 메소드가 호출 되었는지 확인
        • never() - 호출되지 않았는지 여부 검증
        • atLeastOnce() - 최소 한 번은 특정 메소드가 호출되었는지 확인
        • atLeast() - 최소 지정한 횟수 만큼 호출되었는지 확인
        • atMost() - 최대 지정한 횟수 만큼 호출되었는지 확인
        • clear() - stub을 초기화 한다
        • timeOut() - 지정된 시간 안에 호출되었는지 확인
    • Request 관련 테스트: HttpServletRequest, HttpServletResponse -> MockHttpServletRequest, MockHttpServletResponse 을 사용! (아직 사용해보지는 않았음)

    • NoSuchBeanDefinitionException 에러주의) 컨트롤러에서 사용한 레포,서비스 등등 은 꼭 Mock해주자. 테스트코드에는 없고 컨트롤러에서만 사용중이어도 무조건 @MockBean 등록은 일단 필수다!!

      • 예로 @Test 회원가입() 로직에 memberService만 사용하고 exp, character서비스는 사용하지 않았다.
      • 근데, 실제 register() 로직인 “회원가입 컨트롤러 메서드” 코드를 보면 exp, character서비스 전부 사용한다.
      • 그렇다면 아래처럼 @MockBean 등록은 일단 필수다!!

      image

    • mockMvc.perform 함수에서 json 내용을 체크 하고 싶을때? -> jsonPath()

      • 배열 처리는? .andExpect(jsonPath("$[0].content").value("알림테스트1")) // 응답 body 의 json 확인 .andExpect(jsonPath("$[2].content").exists()) .andExpect(jsonPath("$[3]").doesNotExist()) // 없어야 정상
      • 일반 적인 {}는? .andExpect(*jsonPath*("$.data.uid").value("12345")) // 응답 body 의 json 확인


예시 코드 참고

도메인
@Slf4j
class MemberTest {
  @Test
  public void 생성_편의메서드() throws Exception {
    // given
    Member member = null;
    // when
    member = Member.createMember("test", "테스트1");
    // then
    Assertions.assertInstanceOf(Member.class, member);
  }
//
  @Test
  public void 연관관계_편의메서드() throws Exception {
    // given
    Member member = Member.createMember("test", "테스트2");
    Lists lists = Lists.createLists(member, LocalDateTime.now(), new ArrayList<>());
    log.info("{}", member.getLists().size()); //=0
    // when
    member.addLists(lists);
    log.info("{}", member.getLists().size()); //=1
    // then
    Assertions.assertEquals(member.getLists().size(), 1);
  }
}
레포지토리
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@SpringBootTest
@Slf4j
class MemberRepositoryTest {
  @Autowired
  MemberRepository memberRepository;
  @Autowired
  EntityManager em;
//
  /**
   * save, findOne, findByUid, findAllWithPage
   */
  @Test
  @Order(1)
  @Transactional // 롤백
  @Rollback(value = false)
  public void 회원가입_조회() throws Exception {
    // given
    Member member = Member.createMember("test", "test1");
    // 혹시모를 FK 에러 방지
    Exp exp = Exp.createExp(0L, 0L, 1L);
    em.persist(exp); // FK id 위해
    Character character = Character.createCharacter(exp, new ArrayList<>(), new ArrayList<>(),
        new ArrayList<>());
    em.persist(character); // FK id 위해
    member.setCharacter(character);
//
    // when
    memberRepository.save(member); // persist
    log.info("{}", member.getId());
//        em.flush(); // 롤백 true 때문에 insert 쿼리 생략시 flush 추가로 볼 수 있음
//        em.clear(); // flush 사용시 이것까지 해줘야 아래 select 문 전송 쿼리도 볼 수 있음
    Member findMember = memberRepository.findOne(member.getId());
    log.info("{}", member.getId());
//
    // then
    Assertions.assertEquals(member.getId(), findMember.getId());
    // 단, 위에서 em.clear를 한 경우 영속성이(캐시) 비어있으므로 findMember가 새로운 주소!!
    // 따라서 아래 출력으로 틀리다는 결론이 나온다.
    Assertions.assertEquals(member, findMember);
  }
//
  @Test
  @Order(2)
  @Transactional
  public void 회원조회_UID() throws Exception {
    // given
    Member findMember;
    // when
    findMember = memberRepository.findByUid("test");
    Member findMember2 = memberRepository.findByUid("testtest");
    // then
    Assertions.assertEquals(findMember.getNickname(), "test1");
    Assertions.assertEquals(findMember2, null);
  }
//
  @Test
  @Order(3)
  @Transactional
  public void 회원조회_Page() throws Exception {
    // given
    List<FindMemberResponseDto> memberList = new ArrayList<>();
    // when
    memberList = memberRepository.findAllWithPage(1); // order by desc
    log.info("memberList : {}", memberList.size());
    log.info("memberList.get(0) : {}", memberList.get(0));
    // then
    for (FindMemberResponseDto dto : memberList) {
      log.info("member id : {}, nickname : {}", dto.getId(), dto.getNickname());
    }
    Assertions.assertEquals(memberList.get(0).getNickname(), "test1");
  }
}
서비스
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@SpringBootTest
@Transactional // 쓰기모드 -> 서비스코드에 트랜잭션 유무 반드시 확인
@Slf4j
public class MemberServiceTest {
  @Autowired
  EntityManager em;
  @Autowired
  MemberService memberService;
  static final String UID = "12345";
  static final String MESSAGE = "이미 존재하는 회원입니다.";
  static Long memberId;
//
  /**
   * join(중복검증 포함), findOne, findByUid, {findAllWithPage, initCacheMembers}(=회원 최신순_페이징 조회+캐시)
   *
  @Test
  @Order(1)
  @Rollback(value = false)
  public void 회원가입_조회() throws Exception {
    // given
    Member member = Member.createMember(UID, "테스트 닉네임");
    Exp exp = Exp.createExp(0L, 0L, 1L);
    em.persist(exp);
    Character character = Character.createCharacter(exp, new ArrayList<>(), new ArrayList<>(),
        new ArrayList<>());
    em.persist(character);
    member.setCharacter(character);
    // when
    memberService.join(member);
    Member findMember = memberService.findOne(member.getId());
    Member findMember2 = memberService.findByUid(UID);
    // then
    Assertions.assertEquals(member.getId(), findMember.getId());
    Assertions.assertEquals(member.getId(), findMember2.getId());
    memberId = member.getId();
  }
//
  @Test
  @Order(2)
  public void 중복검증_예외() throws Exception {
    // given
    Member member = memberService.findOne(memberId);
    // when
    // then
    Throwable exception = Assertions.assertThrows(IllegalStateException.class, () -> {
      memberService.join(member); // 예외발생 로직
    });
    Assertions.assertEquals(MESSAGE, exception.getMessage());
    log.info("exception.getMessage() : {}", exception.getMessage());
  }
//
  @Test
  @Order(3)
  public void 회원_페이징_캐시_조회() throws Exception {
    // given
    // when
    List<FindMemberResponseDto> dto = memberService.findAllWithPage(1);
    log.info("캐시되었으면 쿼리 안날라감1");
    memberService.findAllWithPage(1);
    log.info("캐시되었으면 쿼리 안날라감2");
    memberService.findAllWithPage(1);
    log.info("캐시되었으면 쿼리 안날라감3");
    memberService.initCacheMembers(); // 캐시 초기화
    log.info("캐시 초기화 했으므로 쿼리 날라가야 함");
    memberService.findAllWithPage(1);
    // then
    for (FindMemberResponseDto m : dto) {
      log.info("member.id : {}", m.getId());
      log.info("member.nickName : {}", m.getNickname());
    }
  }
}
컨트롤러
@WebMvcTest(controllers = MemberApiController.class)
class MemberApiControllerTest {
    @Autowired
    MockMvc mockMvc;
    @MockBean // 가짜 객체이므로 실제 동작은 X
    ExpService expService;
    @MockBean
    CharacterService characterService;
    @MockBean
    MemberService memberService;
//
    /**
     * login, register, logout, findAllWithPage
     */
    @Test
    public void 회원가입() throws Exception {
        // given
        String content = "{\"uid\":\"12345\", \"nickname\":\"회원가입 테스트\"}"; //입력 json 흉내
//        Member member = Member.createMember("12345", "회원가입 테스트");
//        member.setId(1L);
        // Mocking Member 객체
        Member member = mock(Member.class);
        when(member.getId()).thenReturn(1L); // ID 반환 설정
        when(member.getUid()).thenReturn("12345");
        when(member.getNickname()).thenReturn("회원가입 테스트");
        // when
        when(memberService.join(any())).thenReturn(member);
        mockMvc.perform(
                        post("/api/v1/members/register")
                                .contentType(MediaType.APPLICATION_JSON)
                                .accept(MediaType.APPLICATION_JSON)
                                .characterEncoding("UTF-8")
                                .content(content)
                )
                .andExpect(status().isCreated()) // 예상 응답
                .andExpect(jsonPath("$.data.uid").value("12345")) // 응답 body 의 json 확인
                .andDo(print());
        // then
        verify(memberService).join(any());
        verify(expService).join(any());
        verify(characterService).join(any());
    }
//
    @Test
    public void 로그인() throws Exception {
        // given
        String content = "{\"uid\":\"123\"}"; // {"name":"value"} 형태로 작성해야 JSON 형태!
//        Member member = Member.createMember("123", "test");
//        member.setId(1L);
        // Mocking Member 객체
        Member member = mock(Member.class);
        when(member.getId()).thenReturn(1L); // ID 반환 설정
        when(member.getUid()).thenReturn("123");
        when(member.getNickname()).thenReturn("로그인 테스트");
        // when
        when(memberService.findByUid(any())).thenReturn(member);
        mockMvc.perform(
                        post("/api/v1/members/login")
                                .contentType(MediaType.APPLICATION_JSON)
                                .accept(MediaType.APPLICATION_JSON)
                                .characterEncoding("UTF-8")
                                .content(content)
                )
                .andExpect(status().isOk()) // 예상 응답
                .andDo(print());
        // then
        verify(memberService).findByUid(any());
    }
//
    @Test
    public void 로그아웃_성공과중복() throws Exception {
        // given
//        Member member = Member.createMember("123", "test");
//        member.setId(1L);
        // Mocking Member 객체
        Member member = mock(Member.class);
        when(member.getId()).thenReturn(1L); // ID 반환 설정
        when(member.getUid()).thenReturn("123");
        when(member.getNickname()).thenReturn("로그아웃 테스트");
        MockHttpSession session = new MockHttpSession();
        session.setAttribute(SESSION_NAME_LOGIN, member.getId());
        // when
        mockMvc.perform(
                        post("/api/v1/members/logout")
                                .session(session)
                                .contentType(MediaType.APPLICATION_JSON)
                                .accept(MediaType.APPLICATION_JSON)
                                .characterEncoding("UTF-8")
                )
                .andExpect(status().isOk()); // 로그아웃 성공
        mockMvc.perform(
                        post("/api/v1/members/logout")
                                .contentType(MediaType.APPLICATION_JSON)
                                .accept(MediaType.APPLICATION_JSON)
                                .characterEncoding("UTF-8")
                )
                .andExpect(status().isConflict()); // 로그아웃 중복
        // then
    }
//
    @Test
    public void 멤버_조회_페이징() throws Exception {
        // given
        int pageId = 1;
        MockHttpSession session = new MockHttpSession();
        session.setAttribute(SESSION_NAME_LOGIN, 1); // 회원 인증 인터셉터 통과위해
        // 테스트용 members 세팅
        List<FindMemberResponseDto> members = new ArrayList<>();
        FindMemberResponseDto m1 = new FindMemberResponseDto(111L, "테스트1", 1L);
        FindMemberResponseDto m2 = new FindMemberResponseDto(222L, "테스트2", 1L);
        FindMemberResponseDto m3 = new FindMemberResponseDto(333L, "테스트3", 1L);
        members.add(m1);
        members.add(m2);
        members.add(m3);
        // when
        when(memberService.findAllWithPage(1)).thenReturn(members);
        mockMvc.perform(
                        get("/api/v1/members/" + pageId)
                                .session(session)
                                .contentType(MediaType.APPLICATION_JSON)
                                .accept(MediaType.APPLICATION_JSON)
                                .characterEncoding("UTF-8")
                )
                .andExpect(status().isOk()) // 예상 응답
                .andDo(print());
        // then
        verify(memberService).findAllWithPage(1);
    }
} 

페이징 테스트 코드는 “MyBatis + Spring 파트” 게시물 참고



댓글남기기