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 상태 코드를 받습니다.


궁금한신점이나 잘못된 부분이 있으면 자유롭게 댓글 달아주세요.

github : https://github.com/jaeyeon423/spring_board_api