spring/게시판 api
Spring boot 게시판 API 서버 제작 (10) - 로그인 - 인증 로직 테스트
얼킴
2023. 2. 21. 00:10
이번에는 이전 시간에 만든 인증 로직을 테스트 해보겠습니다. 원래대로라면 코드만 보여드리고 설명을 생략하겠지만 이전에 만든 인증 로직에 대한 이해를 돕고자 테스트를 따로 준비했습니다.
테스트를 진행하기에 앞서 데이터베이스 초기화를 위한 TestInitDB를 생성하겠습니다.
데이터베이스 초기화
@Component
public class TestInitDB {
@Autowired RoleRepository roleRepository;
@Autowired MemberRepository memberRepository;
@Autowired PasswordEncoder passwordEncoder;
private String adminEmail = "admin@admin.com";
private String member1Email = "member1@member.com";
private String member2Email = "member2@member.com";
private String password = "123456a!";
@Transactional
public void initDB() {
initRole(); // RoleType enum의 모든 값을 role에 저장
initTestAdmin(); //관리자 계정 추가
initTestMember(); // 멤버 계정 추가
}
private void initRole() {
roleRepository.saveAll(
List.of(RoleType.values()).stream().map(roleType -> new Role(roleType)).collect(Collectors.toList())
);
}
private void initTestAdmin() {
memberRepository.save(
new Member(adminEmail, passwordEncoder.encode(password), "admin", "admin",
List.of(roleRepository.findByRoleType(RoleType.ROLE_NORMAL).orElseThrow(RoleNotFoundException::new),
roleRepository.findByRoleType(RoleType.ROLE_ADMIN).orElseThrow(RoleNotFoundException::new)))
);
}
private void initTestMember() {
memberRepository.saveAll(
List.of(
new Member(member1Email, passwordEncoder.encode(password), "member1", "member1",
List.of(roleRepository.findByRoleType(RoleType.ROLE_NORMAL).orElseThrow(RoleNotFoundException::new))),
new Member(member2Email, passwordEncoder.encode(password), "member2", "member2",
List.of(roleRepository.findByRoleType(RoleType.ROLE_NORMAL).orElseThrow(RoleNotFoundException::new))))
);
}
public String getAdminEmail() {
return adminEmail;
}
public String getMember1Email() {
return member1Email;
}
public String getMember2Email() {
return member2Email;
}
public String getPassword() {
return password;
}
}
- initRole() : RoleType enum의 모든 값을 role에 저장
- initTestAdmin() : 관리자 계정 추가
- initTestMember() : 멤버 계정 추가
인증 로직 테스트
일단 바로 코드부터 보겠습니다.
@SpringBootTest
@AutoConfigureMockMvc // MockMvc를 자동으로 구성하여 사용
@ActiveProfiles(value = "test") // local과 겹치지 않도록
@Transactional
class MemberControllerIntegrationTest {
@Autowired
WebApplicationContext context;
@Autowired
MockMvc mockMvc; // HTTP 요청을 수행하고, 응답을 검증하는 데 사용되며 컨트롤러의 동작 시물레이션이 가능
@Autowired
TestInitDB initDB;
@Autowired
SignService signService;
@Autowired
MemberRepository memberRepository;
@BeforeEach
void beforeEach() {
mockMvc = MockMvcBuilders.webAppContextSetup(context).apply(springSecurity()).build();
initDB.initDB();
}
@Test
void readTest() throws Exception {
// given
Member member = memberRepository.findByEmail(initDB.getMember1Email()).orElseThrow(MemberNotFoundException::new);
// when, then
mockMvc.perform(
get("/api/members/{id}", member.getId()))
.andExpect(status().isOk());
}
@Test
void deleteTest() throws Exception {
// given
Member member = memberRepository.findByEmail(initDB.getMember1Email()).orElseThrow(MemberNotFoundException::new);
SignInResponse signInRes = signService.signIn(new SignInRequest(initDB.getMember1Email(), initDB.getPassword()));
// when, then
mockMvc.perform(
delete("/api/members/{id}", member.getId())
.header("Authorization", signInRes.getAccessToken())) // HTTP Header에 access token을 발급
.andExpect(status().isOk());
}
@Test
void deleteByAdminTest() throws Exception {
// given
Member member = memberRepository.findByEmail(initDB.getMember1Email()).orElseThrow(MemberNotFoundException::new);
SignInResponse adminSignInRes = signService.signIn(new SignInRequest(initDB.getAdminEmail(), initDB.getPassword()));
// when, then
mockMvc.perform(
delete("/api/members/{id}", member.getId()).header("Authorization", adminSignInRes.getAccessToken()))
.andExpect(status().isOk());
}
@Test
void deleteUnauthorizedByNoneTokenTest() throws Exception {
// given
Member member = memberRepository.findByEmail(initDB.getMember1Email()).orElseThrow(MemberNotFoundException::new);
// when, then
mockMvc.perform(
delete("/api/members/{id}", member.getId()))
.andExpect(status().is3xxRedirection())
.andExpect(redirectedUrl("/exception/entry-point"));
}
@Test
void deleteAccessDeniedByNotResourceOwnerTest() throws Exception {
// given
Member member = memberRepository.findByEmail(initDB.getMember1Email()).orElseThrow(MemberNotFoundException::new);
SignInResponse attackerSignInRes = signService.signIn(new SignInRequest(initDB.getMember2Email(), initDB.getPassword()));
// when, then
mockMvc.perform(
delete("/api/members/{id}", member.getId()).header("Authorization", attackerSignInRes.getAccessToken()))
.andExpect(status().is3xxRedirection())
.andExpect(redirectedUrl("/exception/access-denied"));
}
@Test
void deleteAccessDeniedByRefreshTokenTest() throws Exception {
// given
Member member = memberRepository.findByEmail(initDB.getMember1Email()).orElseThrow(MemberNotFoundException::new);
SignInResponse signInRes = signService.signIn(new SignInRequest(initDB.getMember1Email(), initDB.getPassword()));
// when, then
mockMvc.perform(
delete("/api/members/{id}", member.getId()).header("Authorization", signInRes.getRefreshToken()))
.andExpect(status().is3xxRedirection())
.andExpect(redirectedUrl("/exception/access-denied"));
}
}
이번 테스트에서 중요하다고 생각되는 delete 부분을 살펴 보겠습니다.
deleteTest
@Test
void deleteTest() throws Exception {
// given
Member member = memberRepository.findByEmail(initDB.getMember1Email()).orElseThrow(MemberNotFoundException::new);
SignInResponse signInRes = signService.signIn(new SignInRequest(initDB.getMember1Email(), initDB.getPassword()));
// when, then
mockMvc.perform(
delete("/api/members/{id}", member.getId())
.header("Authorization", signInRes.getAccessToken())) // HTTP Header에 access token을 발급
.andExpect(status().isOk());
}
로그인 후에 발급받은 access token을 Authorization 헤더에 포함해서 요청하면 정상적으로 처리가 되어 200 상태 응답 코드를 받습니다.
deleteByAdminTest
@Test
void deleteByAdminTest() throws Exception {
// given
Member member = memberRepository.findByEmail(initDB.getMember1Email()).orElseThrow(MemberNotFoundException::new);
SignInResponse adminSignInRes = signService.signIn(new SignInRequest(initDB.getAdminEmail(), initDB.getPassword()));
// when, then
mockMvc.perform(
delete("/api/members/{id}", member.getId()).header("Authorization", adminSignInRes.getAccessToken()))
.andExpect(status().isOk());
}
로그인 한 사용자가 관리자일 경우에도 동일하게 정상적으로 처리가 되어 200 상태 코드를 받습니다.
궁금한신점이나 잘못된 부분이 있으면 자유롭게 댓글 달아주세요.