ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Spring Boot] 팀 생성, DB 슬라이스 테스트
    Backend/개발 2021. 8. 5. 01:49
    반응형

    TIL 36일차 개인프로젝트 

    오늘은 할 수 있던 시간 대비 공부한내용이나 구현한게 별로 많지 않다.. 많이 쉬었다..ㅠ_ㅠ

    이렇게 쪼금씩쪼금씩 하면 거의 글 50개써야 겨우 완성될 것 같은데,, 사실 그래서 TIL 카테고리에 넣은거니까,,ㅎ 나중에 블로그용으로 쓰게되면 공부한거랑 구현한거 분리해서 다시 정리해야될 것 같다다ㅏㅏ...



    DB에 값 넣기

    36-1

    일단 DB에 참가자가 등록 된 후의 구현을 먼저 해야할 것 같아서 넣어줬다. 총 20명

     

    다음 순서로 진행된다.

    1. 일괄적으로 특정 라인의 참가자를 모두 불러와 팀에 등록하며 leader로 만들어준다.
      1. 이때 등록되는 참가자는 point가 존재하지 않는다(팀장이므로)
      2. 이때 팀이 순서대로 생성된다.
    2. 경매가 진행된다
      1. 특정 참가자가 랜덤으로 추출된다
      2. 채팅을 통해서 경매가 진행되고 타이머가 끝나면 낙찰된다
      3. 유찰의 경우 point에 체크해준뒤 종료된다.
    3. 경매 성공한 팀 / 낙찰된 참가자가 있다
      1. 팀에 참가자를 등록하고 경매 포인트만큼 차감한다.
      2. 참가자에 팀을 등록한다.

    2-3을 반복한다.

    1. 동일 포지션에서 1명을 제외한 나머지 참가자가 팀이 결정되면 나머지는 자동으로 결정된다. (유찰이 된 참가자도 포함)
    2. 모든 참가자가 종료되고, 유찰된 참가자만이 모인다.

     


    팀 생성

    미드라이너를 팀장으로 팀을 생성해보았다.

    @Transactional
    public void setTeamLeaderByPosition(String position){
      List<ParticipantsEntity> participants = participantsRepository.findParticipantsEntitiesByMainPosition(position);
    
      for(ParticipantsEntity entity: participants){
        entity.updatePoint(0l);
        TeamsEntity teamsEntity = TeamsEntity.builder()
          .teamName(entity.getSummonerName()+"팀")
          .leaderId(entity.getId())
          .build();
        teamsEntity.addParticipants(entity);
        teamsRepository.save(teamsEntity);
      }
    }
    @Test
    public void 팀생성(){
      // given
      String position = "MID";
    
      // when
      teamsService.setTeamLeaderByPosition(position);
    
      // then
      List<TeamsEntity> teamsEntities = teamsRepository.findAll();
      assertThat(teamsEntities.size()).isEqualTo(4);
    }
    
    //BUILD SUCCESSFUL in 14s
    //4 actionable tasks: 3 executed, 1 up-to-date
    //11:26:10 오후: Task execution finished ':test --tests "com.project.auction.lol.service.TeamsServiceTest.팀생성"'.
    

    36-236-3

    참가자와 팀이 연결되고, point를 0으로 지정해주었다.

     

    나중에는 이걸 API로 만들어서 처리해주면 좋을 것 같아서 컨트롤러도 작성해보았다.

    @GetMapping("/teams/leader/{position}")
    public ResponseEntity setTeamLeaderByPosition(@PathVariable String position){
    
      String reg = "TOP|JUG|MID|ADC|SUP";
      position = position.toUpperCase();
      if(!Pattern.matches(reg, position)) return new ResponseEntity(HttpStatus.BAD_REQUEST);
    
      try {
        teamsService.setTeamLeaderByPosition(position);
        return new ResponseEntity(HttpStatus.OK);
      }
      catch (Exception e){
        String message = "이미 팀이 생성되었습니다.";
        return new ResponseEntity(message, HttpStatus.CONFLICT);
      }
    }
    • 올바르지 않은 포지션이 입력으로 들어왔을 경우 bad_request, 이미 팀이 만들어진 상태일 경우 conflict로 에러를 처리하였다.
    • Pattern.matches(정규식, 검색할문자) 는 boolean을 반환한다.
    • 테스트 코드를 작성 할 때는 @WebMvcTest(controllers = TeamsController.class)를 사용해서 테스트의 범위를 service등이 아닌 controller로만 제한해주었다.

     

    몇가지 예외처리를 controllerTest에서 webmvctest로 테스트해봤는데 안먹는거 보니, service단에서 throw를 날렸을 경우 controller에서 잡지 못했다. 실제로 어플리케이션을 돌리면 되는데 테스트코드에서 안되는 이유가 있을 것 같은데 찾지 못했다. ㅠ

    보통 컨트롤러 내부에서 발생한 execption을 따로 처리하지 않고 바깥으로 내보내서 전역적으로 예외처리를 해주는 것 같다. 예외처리 할 일이 많아지면 한번에 해줘야겠다. 또 미루기..^^;ㅠ


    MockMvc

    테스트에 필요한 기능만 가지는 가짜 객체를 만들어 서버에 배포하지 않고 스프링 MVC 동작을 재현할 수 있는 클래스이다.

     

    MockMvc method
    • perform()
      • 요청을 전송하는 역할
      • 결과로 ResultActions객체를 받으며 리턴값을 검증하고 확인할 수 있는 andExpect를 제공한다.
    • andExpect()
      • 응답을 검증하는 역할
      • 상태코드 status()
      • 뷰, 리다이렉트, 모델정보, 응답정보
    • andDo(print())
      • 요청/응답 전체메세지 확인

    JpaMappingContext error

    org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jpaAuditingHandler': Cannot resolve reference to bean 'jpaMappingContext' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jpaMappingContext': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: JPA metamodel must not be empty!

    테스트를 하려고하는데 다음과같은 에러가 발생했다.

     

    Auditing은 ParticipantsEntity에 BaseTimeEntity auditing 설정을 한 부분이 있었는데, 거기서 에러가 난 것 같다. 해당 기능을 사용하기 위해 Application 파일에 @EnableJpaAuditing 어노테이션으 설정해 주었는데, webmvctest같은 슬라이스 테스트에서는 해당 bean을 로드하지 않기 때문에 생긴 오류라고 한다.


    @MockBean(JpaMetamodeMappingContext.class)를 추가하여 해결해주었다. 따로 configuration을 분리하는 방법 도 있는데, 나중에 슬라이스 테스트 클래스가 많아질 경우 처리해주면 될 것 같다.


    슬라이스 테스트

    레이어 별로 잘라서 하는 단위테스트이다.

     

    예를들어 MockMvc를 이용해 controller만을 테스트 할 때 Controller가 사용할 Service를 Mock함으로써(가짜 빈으로) 데이터 레이어와 상관 없이 컨트롤러의 동작이나 유효성만을 검사할 수 있다.

    슬라이스 테스트를 하는 이유
    • @SpringBootTest 어노테이션의 단점
      • 모든 Bean을 로드하기 때문에 시간이 오래거린다.
      • 테스트의 단위가 커서 테스트코드를 통한 빠른 피드백의 장점이 희석된다.
    슬라이스 테스트 종류
    • @WebMvcTest
      • @Controller,@ControllerAdvice,@JsonComponent,@WebMvcConfigurer등만 Bean으로 등록한다. ( service, repository는 bean으로 등록하지 않음 )
      • 보통 @Autowired MockMvc @MockBean xxxService처리를 해준다. xxxService는 해당 컨트롤러에서 사용하는 서비스
    • @WebFluxTest
    • @DataJpaTest
    • etc..

    // 이미 팀이 생성되었다면
    if(!teamsRepository.findAll().isEmpty()) throw new Exception();

    service에 예외처리를 더 해주었다.

    다 불러와야하는게 좀 별로긴한데.. count를 해도 더 복잡한 쿼리가 나갈 것 같고,, queryDsl에서 selectOne이나, query를 직접 작성해서 select 1 limit1 을 하면 되는 것 같다. - 참고 jojoldu- JPA exists 쿼리 성능 개선

     

    그런데 JPQL에선 limit을 지원하지 않는 것 같다. queryDsl을 사용할 때 수정해주어야 겠다.

    출처

    mvc 관련

    스프링부트 테스트코드작성

    ResponseEntity

    RESTful API 설계 가이드

    Spring boot테스트에러 : JPA metamodel must not be empty

    슬라이스테스트

    댓글

Designed by Tistory.