개발이 좋아서/Troubleshooting이 좋아서

@WithCustomMockUser - 테스트 코드 작성 시 인증

zoaseo 2025. 2. 24. 17:11

컨트롤러단에서 @AuthenticationPrincipal을 이용해 id값을 가져와 쓰고 있었다.

테스트 코드를 작성하는데 id값을 어떻게 가져와야 할 지 몰랐다. 그래서 @WithCustomMockUser 어노테이션을 만들어 사용하였다.

package com.swyp.libri.domain.annotation;

import org.springframework.security.test.context.support.WithSecurityContext;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
@WithSecurityContext(factory = WithCustomMockUserSecurityContextFactory.class)
public @interface WithCustomMockUser {
    String email() default "test@example.com";
    String role() default "ROLE_USER";
    long id() default 1L;
}
package com.swyp.libri.domain.annotation;

import com.swyp.libri.domain.model.Member;
import com.swyp.libri.global.oauth2.CustomUserPrincipal;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.test.context.support.WithSecurityContextFactory;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.test.util.ReflectionTestUtils;

import java.util.Collections;

public class WithCustomMockUserSecurityContextFactory implements WithSecurityContextFactory<WithCustomMockUser> {

    @Override
    public SecurityContext createSecurityContext(WithCustomMockUser customUser) {
        SecurityContext context = SecurityContextHolder.createEmptyContext();

        Member member = new Member(customUser.email(), "provider", "id");

        ReflectionTestUtils.setField(member, "id", customUser.id());

        CustomUserPrincipal principal = new CustomUserPrincipal(
                member,
                Collections.emptyMap(),
                Collections.singletonList(new SimpleGrantedAuthority(customUser.role()))
        );

        Authentication authentication = new TestingAuthenticationToken(principal, null);
        authentication.setAuthenticated(true);
        context.setAuthentication(authentication);
        return context;
    }
}

 

 

테스트 코드

package com.swyp.libri.domain.controller;

import com.swyp.libri.domain.annotation.WithCustomMockUser;
import com.swyp.libri.domain.auth.dto.UniqueIdRequest;
import com.swyp.libri.domain.model.Member;
import com.swyp.libri.domain.repository.MemberRepository;
import com.swyp.libri.domain.service.MemberService;
import com.swyp.libri.global.oauth2.CustomUserPrincipal;
import com.swyp.libri.global.util.ApiDocumentation;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.MediaType;
import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper;

import java.security.Principal;
import java.util.Optional;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@WithCustomMockUser
@AutoConfigureRestDocs
@WebMvcTest(MemberController.class)
class MemberControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockitoBean
    private MemberService memberService;

    private static final String URL = "/member";
    private static final String TAG = "MEMBER";

    @Test
    void createUniqueId() throws Exception {

        Long memberId = 1L;
        String uniqueId = "unique123";

        doNothing().when(memberService).createUniqueId(memberId, uniqueId);

        ResultActions actions = mockMvc.perform(RestDocumentationRequestBuilders.post(URL + "/create/unique-id")
                .content(new ObjectMapper().writeValueAsString(new UniqueIdRequest(uniqueId)))
                .contentType(MediaType.APPLICATION_JSON)
                .accept(MediaType.APPLICATION_JSON)
                .with(csrf().asHeader()));

        actions.andExpect(status().isOk())
                .andDo(ApiDocumentation.builder()
                        .tag(TAG)
                        .description("아이디 생성 API")
                        .requestFields(
                                fieldWithPath("uniqueId").description("생성할 유니크 ID")
                        )
                        .build())
                .andDo(print());

        verify(memberService).createUniqueId(memberId, uniqueId);
    }
}

@WithCustomMockUser로 컨트롤러단에서 테스트 코드를 만들 수 있었다.