테스트 코드를 작성하면서

jungnoeun·2023년 1월 19일
0

kiri

목록 보기
3/13

스프링 시큐리티를 사용해서 로그인을 구현하고, 인증이 된 사용자에 한해서 사용이 가능하도록 몇몇 페이지의 접근을 제한하였다.
이 제한있는 페이지들을 구현하면서, 테스트 코드를 작성하지 않을 수가 없었는데, 로그인이 된 사용자들을 어떻게 처리해야 할지 고민이었다.
원래대로면, 회원가입 -> 로그인 -> 인증 요구 페이지 순대로 해야 접근이 가능하는데, 앞으로 많은 인증요구 페이지를 작성하면서 이 과정을 일일히 다 쓰는것은 비효율적이라고 생각했다.

그래서 열심히 구글링을 한 결과,
스프링 시큐리티에서는 인증 관련 어노테이션을 제공하였다.
@WithMockUser 와 같은 어노테이션은 jwt를 사용하여, 헤더를 통해 토큰을 주고받는 나에게 적합한 방법이 아니었고,
좀 더 유연하게 원하는 Authentication을 사용하고자 한다면, @WithSecurityContext를 사용하는 것이 좋다고 생각되었다.



먼저 어노테이션을 만들어주고,

@Retention(RetentionPolicy.RUNTIME)
@WithSecurityContext(factory = WithAccountSecurityContextFactory.class)
public @interface WithAccount {
    String value();
}

그다음에, @WithSecurityContext애노테이션에 전달한 WithMockCustomUserSecurityContextFactory클래스를 만든다.

public class WithAccountSecurityContextFactory implements WithSecurityContextFactory<WithAccount> {

    @Autowired
    MemberService memberService;

    @Autowired
    PrincipalDetailsService principalDetailsService;

    @Override
    public SecurityContext createSecurityContext(WithAccount annotation) {

        String username = annotation.value();
        String email = username + "@aaa.com";
        String password = "abcdefgh1234";
        String interest = "기타";

        Member member = Member.builder()
                .email(email)
                .username(username)
                .password(password)
                .interest(interest)
                .build();

        memberService.postMember(member);

        UserDetails userDetails = principalDetailsService.loadUserByUsername(email);
        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, userDetails.getPassword(), userDetails.getAuthorities());
        SecurityContext context = SecurityContextHolder.createEmptyContext();
        context.setAuthentication(authentication);

        return context;
    }
}

그리고 이렇게 만든 어노테이션을 테스트 코드에 적용하여, 로그인 과정을 따로 작성해주지 않고, 인증이 요구되는 기능을 테스트해볼 수 있다.

@SpringBootTest
@Transactional
@Rollback(false)
@@ -23,6 +34,65 @@ class MemberTest {
    @PersistenceContext
    EntityManager em;

    @Autowired MemberService memberService;
    @Autowired MemberRepository memberRepository;


    @AfterEach
    void afterEach() {
        memberRepository.deleteAll();
    }

    /**
     * username : creamyyy
     * email : creamyyy@aaa.com
     * password : abcdefgh1234
     * interest = 기타
     */
    @DisplayName("개인 정보 수정 테스트")
    @WithAccount("creamyyy")
    @Test
    void updateMyMember() throws Exception {
        //given
        Member member = memberRepository.findByEmail("creamyyy@aaa.com").get();
        Long id = member.getId();
        String beforePassword = member.getPassword();

        String changePassword = "aaaaaaa444";
        member.changePassword(changePassword);

        //when
        Member afterMember = memberService.updateMember(member, id);

        //then
        assertThat(beforePassword).isNotEqualTo(afterMember.getPassword());
        System.out.println("==========================================================================");
        System.out.println("beforePassword = " + beforePassword);
        System.out.println("afterMember.password = " + afterMember.getPassword());

    }
}

참고
WithSecurityContextFactory 구현체에서 매번 새로운 계정을 생성해주는 것이기 때문에 @AfterEach 어노테이션을 통해 테스트가 끝날 때마다 계정을 삭제해줘야 한다. 삭제를 해주지 않을 경우 아이디가 중복되어 에러가 발생하였다.




참고 블로그
https://gracelove91.tistory.com/106

다음 페이지
https://velog.io/@jungnoeun/%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%BD%94%EB%93%9C%EB%A5%BC-%EC%9E%91%EC%84%B1%ED%95%98%EB%A9%B4%EC%84%9C..2-a0f38we2

profile
개발자

0개의 댓글