Junit Test Application-33-계좌삭제 기능JUnit 테스트

jaegeunsong97·2023년 8월 8일
0

Junit Bank Application 깃허브

Junit Bank Application 기록 노션

  • 서비스 테스트 코드
@Test
     public void 계좌삭제_test() throws Exception {
          // given
          Long number = 1111L;
          Long userId = 2L;

          // stub 1
          User ssar = newMockUser(1L, "ssar", "쌀");
          Account ssarAccount = newMockAccount(1L, 1111L, 1000L, ssar);
          when(accountRepository.findByNumber(any())).thenReturn(Optional.of(ssarAccount));

          // when
          accountService.계좌삭제(number, userId);

          // then
          assertThrows(CustomApiException.class, () -> accountService.계좌삭제(number, userId));
     }
  • 컨트롤러 테스트 코드
@BeforeEach
     public void setUp() {
          User ssar = userRepository.save(newUser("ssar", "쌀"));
          User cos = userRepository.save(newUser("cos", "코스"));

          Account ssarAccount1 = accountRepository.save(newAccount(1111L, ssar));
          Account cosAccount1 = accountRepository.save(newAccount(2222L, cos));
     }
     .
     .
     .
@WithUserDetails(value = "ssar", setupBefore = TestExecutionEvent.TEST_EXECUTION)
     @Test
     public void deleteAccount_test() throws Exception {
          // given
          Long number = 2222L;

          // when
          ResultActions resultActions = mvc.perform(delete("/api/s/account/" + number));
          String responseBody = resultActions.andReturn().getResponse().getContentAsString();
          System.out.println("테스트 : " + responseBody);

          // then

     }

쿼리를 보자

2개의 insert, ssar cos

account 2개 insert

1번쨰 select는 @WithUserDetail 때문에 발생

2번쨰 account를 select

account 서비스를 가보면

쿼리 비교

Account의 user는 LAZY이기 때문에 select를 할 때 가져오지 않는다.

이 부분에서 user.getId()가 있지만 LAZY를 하지 않았다. 왜 그런걸까??


LAZY여도 값들은 전부 DB에서 가져온다. 단지 user의 id 값만 가져오는 것이지, username, password, email, fullname은 가져오지 않는다.

즉, 다시 말하면 id 값만 조회를 할 때는 LAZY로딩이 발동하지 않고, 그 외의 username, password, email, fullname을 조회할 때는 LAZY 로딩이 발동하는 것이다.

근데 쿼리를 보면

처음 UserDetail에서 ssar을 select 했기 때문에 PC에 ssar 존재

ssarAccount 도 PC에 존재

LAZY 로딩이 발동해도 쿼리를 볼 수 없는 이유는 ssar이 이미 PC에 존재하기 때문이다.

근데 테스트를 할 때는 PC에 커밋된 상태를 전부 비워줘야 한다. 이유는 직접 쿼리를 확인을 해야하기 때문에

따라서 처음 setUp()에 ssar, cos가 PC에 존재하지 않게 clear()를 해주자


@ActiveProfiles("test") // 테스트 모드
@Transactional
@AutoConfigureMockMvc
@SpringBootTest(webEnvironment = WebEnvironment.MOCK)
public class AccountControllerTest extends DummyObject {

     @Autowired
     private MockMvc mvc;
     @Autowired
     private ObjectMapper om;
     @Autowired
     private EntityManager em;
     @Autowired
     private UserRepository userRepository;
     @Autowired
     private AccountRepository accountRepository;

     @BeforeEach
     public void setUp() {
          User ssar = userRepository.save(newUser("ssar", "쌀"));
          User cos = userRepository.save(newUser("cos", "코스"));

          Account ssarAccount1 = accountRepository.save(newAccount(1111L, ssar));
          Account cosAccount1 = accountRepository.save(newAccount(2222L, cos));

          em.clear();
     }
     .
     .
     .
/**
      * 테스트시에는 insert 한것들이 전부 PC에 올라감(영속화)
      * 영속화 된것들을 초기화 해주는 것이 개발 모드와 동일한 환경으로 태스트를 할 수 있게 해준다.
      * 최초 SELECT는 쿼리가 발생하지만! PC에 있으면 1차 캐시
      * LAZY 로딩은 쿼리도 발생함 - PC에 있다면
      * LAZY 로딩할 때, PC 없다면 쿼리가 발생함
      */
@WithUserDetails(value = "cos", setupBefore = TestExecutionEvent.TEST_EXECUTION)
     @Test
     public void deleteAccount_test() throws Exception {
          // given
          Long number = 1111L;

          // when
          ResultActions resultActions = mvc.perform(delete("/api/s/account/" + number));
          String responseBody = resultActions.andReturn().getResponse().getContentAsString();
          System.out.println("테스트 : " + responseBody);

          // then

     }

쿼리확인

ssar, cos insert

ssarAccout, cosAccount insert

em.clear -> PC 비워진 상태

인증을 cos -> cos SELECT 쿼리

accountRepository.findByNumber()

checkOnwer() -> user.getUsername();, lazy loading

테스트 시, insert 된것들을 PC 에서 비우자!!

  • 코드 수정
@WithUserDetails(value = "cos", setupBefore = TestExecutionEvent.TEST_EXECUTION)
     @Test
     public void deleteAccount_test() throws Exception {
          // given
          Long number = 1111L;

          // when
          ResultActions resultActions = mvc.perform(delete("/api/s/account/" + number));
          String responseBody = resultActions.andReturn().getResponse().getContentAsString();
          System.out.println("테스트 : " + responseBody);

          // then, JUnit 테스트에서 delete 쿼리는 DB관련으로 가장 마지막에 실행되면 발동안함.
          assertThrows(CustomApiException.class, () -> accountRepository.findByNumber(number).orElseThrow(
                    () -> new CustomApiException("계좌를 찾을 수 없습니다. ")));
     }

마지막 findByNumber 떄문에 쿼리 발생

JOIN FETCH

SELECT ac FROM Account ac JOIN FETCH ac.user u WHERE ac.number = :number

만약 Account를 가져올 때 계속해서 user를 가져오면 매우 비효율적이다. 따라서 JOIN FETCH를 사용해 미리 SELECT를 한다. 즉 LAZY 한 것들을 쿼리로 제어해서 미리 가져오는 것이다.

profile
블로그 이전 : https://medium.com/@jaegeunsong97

0개의 댓글