package shop.mtcoding.bank.service;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import java.util.Optional;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;
import com.fasterxml.jackson.databind.ObjectMapper;
import shop.mtcoding.bank.config.dummy.DummyObject;
import shop.mtcoding.bank.domain.account.Account;
import shop.mtcoding.bank.domain.account.AccountRepository;
import shop.mtcoding.bank.domain.user.User;
import shop.mtcoding.bank.domain.user.UserRepository;
import shop.mtcoding.bank.dto.account.AccountRequestDto.AccountSaveRequestDto;
import shop.mtcoding.bank.dto.account.AccountResponseDto.AccountSaveResponseDto;
@ExtendWith(MockitoExtension.class)
public class AccountServiceTest extends DummyObject {
@InjectMocks
private AccountService accountService;
@Mock
private UserRepository userRepository;
@Mock
private AccountRepository accountRepository;
@Spy
private ObjectMapper om;
@Test
public void 계좌등록_test() throws Exception {
// given
Long userId = 1L;
AccountSaveRequestDto accountSaveRequestDto = new AccountSaveRequestDto();
accountSaveRequestDto.setNumber(1111L);
accountSaveRequestDto.setPassword(1234L);
// stub 1
User ssar = newMockUser(userId, "ssar", "쌀");
when(userRepository.findById(any())).thenReturn(Optional.of(ssar));
// stub 2
when(accountRepository.findByNumber(any())).thenReturn(Optional.empty());
// stub 3
Account ssarAccount = newMockAccout(1L, 1111L, 1000L, ssar);
when(accountRepository.save(any())).thenReturn(ssarAccount);
// when
AccountSaveResponseDto accountSaveResponseDto = accountService.계좌등록(accountSaveRequestDto, userId);
String resposneBody = om.writeValueAsString(accountSaveResponseDto);
System.out.println("테스트 : " + resposneBody);
// then
assertThat(accountSaveRequestDto.getNumber()).isEqualTo(1111L);
}
}
package shop.mtcoding.bank.web;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.http.MediaType;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.transaction.annotation.Transactional;
import com.fasterxml.jackson.databind.ObjectMapper;
import shop.mtcoding.bank.dto.account.AccountRequestDto.AccountSaveRequestDto;
@ActiveProfiles("test") // 테스트 모드
@Transactional
@AutoConfigureMockMvc
@SpringBootTest(webEnvironment = WebEnvironment.MOCK)
public class AccountControllerTest {
@Autowired
private MockMvc mvc;
@Autowired
private ObjectMapper om;
@Test
public void saveAccount_test() throws Exception {
// given
AccountSaveRequestDto accountSaveRequestDto = new AccountSaveRequestDto();
accountSaveRequestDto.setNumber(9999L);
accountSaveRequestDto.setPassword(1234L);
String requestBody = om.writeValueAsString(accountSaveRequestDto);
System.out.println("테스트 : " + requestBody);
// when
ResultActions resultActions = mvc.perform(post("/api/s/account")
.content(requestBody).contentType(MediaType.APPLICATION_JSON));
String resopnseBody = resultActions.andReturn().getResponse().getContentAsString();
System.out.println("테스트 : " + resopnseBody);
// then
}
}
로그인을 하면된다.
우리가 JWT로 로그인을 할 필요가 없다.
JWT token -> 인증필터 -> 시큐리티 세션 생성
즉 /api/s/account는 시큐리티 세션만 있으면 통과가 가능하다는 것이다.
"로그인을 진행해 주세요" 이부분은 SecurityConfig에서 터진 것이다.
즉 AuthorizationFilter 인가를 통과한 것이다.
그러면 헤더를 안 넣었는데 어떻게 통과를 하는거지? 라는 생각이 들 수 있다.
JWT 토큰 헤더를 가지지 않아도 필터는 통과하지만, 시큐리티 단에서 세션 값 검증에 실패할 것이다.
코드를 보면 토큰이 없으면 chain.doFilter가 실행되서 controller로 진입은 할 것이다. 컨트롤러에서 /api/s/ 부분에서 막히게 되는 것이다.
그리고 나서 아래 부분에서 막히게 되는 것이다.
따라서 테스트를 할 때 토큰을 생성할 필요는 없고 시큐리티 세션을 강제 생성만 하면 된다.
@WithUserDetails(value = "ssar")
@Test
public void saveAccount_test() throws Exception {
터지는 이유 : @WithUserDetails, DB에서 username = ssar 조회를 해서 세션에 담아주는 어노테이션
근데 DB에는 ssar이 없다. 따라서 조회했는데 없기 때문에 터진 것이다.
DB에 ssar을 넣어주면 된다.
그래도 터진다.
이유 : setupbefore = TEST_METHOD, setUp 메서드 실행 전에 수행
따라서 @WithUserDetails이 먼저 동작하는 것이기 때문에 터진다.
그러면 setUp 이후에 @WithUserDetails이 동작하게 만들면 된다
@WithUserDetails(value = "ssar", setupBefore = TestExecutionEvent.TEST_EXECUTION)
@Test
public void saveAccount_test() throws Exception {
이렇게 되면 setUp을 통해 ssar이 DB에 저장, @WithUserDetails를 통해 ssar을 DB에서 조회후 세션에 넣는다. 이렇게 되면 인증쪽은 간단하가.
결론 : 컨트롤러 인증 부분 테스트 , setUp() + @WithUserDetails(value = "ssar", setupBefore = TestExecutionEvent.TEST_EXECUTION)
package shop.mtcoding.bank.web;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.TestExecutionEvent;
import org.springframework.security.test.context.support.WithUserDetails;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.transaction.annotation.Transactional;
import com.fasterxml.jackson.databind.ObjectMapper;
import shop.mtcoding.bank.config.dummy.DummyObject;
import shop.mtcoding.bank.domain.user.UserRepository;
import shop.mtcoding.bank.dto.account.AccountRequestDto.AccountSaveRequestDto;
@ActiveProfiles("test") // 테스트 모드
@Transactional
@AutoConfigureMockMvc
@SpringBootTest(webEnvironment = WebEnvironment.MOCK)
public class AccountControllerTest extends DummyObject {
@Autowired
private MockMvc mvc;
@Autowired
private ObjectMapper om;
@Autowired
private UserRepository userRepository;
@BeforeEach
public void setUp() {
userRepository.save(newUser("ssar", "쌀"));
}
@WithUserDetails(value = "ssar", setupBefore = TestExecutionEvent.TEST_EXECUTION)
@Test
public void saveAccount_test() throws Exception {
// given
AccountSaveRequestDto accountSaveRequestDto = new AccountSaveRequestDto();
accountSaveRequestDto.setNumber(9999L);
accountSaveRequestDto.setPassword(1234L);
String requestBody = om.writeValueAsString(accountSaveRequestDto);
System.out.println("테스트 : " + requestBody);
// when
ResultActions resultActions = mvc.perform(post("/api/s/account")
.content(requestBody).contentType(MediaType.APPLICATION_JSON));
String resopnseBody = resultActions.andReturn().getResponse().getContentAsString();
System.out.println("테스트 : " + resopnseBody);
// then
resultActions.andExpect(status().isCreated());
}
}