Notice
Recent Posts
Recent Comments
Link
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

코딩블로그

멀티 모듈 환경에서 내가 만든 api 테스트해보기 Feat. MockMvc, profileResolver, WithCustomUser 본문

PopcornMate

멀티 모듈 환경에서 내가 만든 api 테스트해보기 Feat. MockMvc, profileResolver, WithCustomUser

_hanbxx_ 2024. 3. 24. 16:52
728x90

내가 작성한 ScreeningController에서 

멀티 모듈환경에서 테스트 코드를 짜는 것은 흔하게 구글링 하면 나오는 설정들과 달라서 설정하는게 조금 힘들었다

먼저 전체적인 테스트 코드 환경을 확인해보자

📙ApiIntegrateProfileResolver

public class ApiIntegrateProfileResolver implements ActiveProfilesResolver {

    @Override
    public String[] resolve(Class<?> testClass) {
        // some code to find out your active profiles
        return new String[] {"test","infra","domain","core"};

    }
}

 

테스트를 실행하면 어떤 profile을 가져올 것인지 정해주는 Resolver이다

📙ApiIntegrateSpringBootTest

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@SpringBootTest(classes = ApiIntegrateTestConfig.class,webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles(resolver = ApiIntegrateProfileResolver.class)
@Documented
public @interface ApiIntegrateSpringBootTest {}

MockMvc를 사용할 것이므로 webEnvironment 선언해서 RANDOM_PORT 설정을 해줘야 한다

📙ApiIntegrateTestConfig

@Configuration
@ComponentScan(basePackageClasses = { ApiApplication.class, InfraApplication.class, CoreApplication.class, DomainApplication.class})
public class ApiIntegrateTestConfig {
}

 

여기서 ComponentScan에서 저렇게 basPackageClasses를 선언해주지 않으면 스프링 부트가 인식하지 못하여 오류가 난다

 

지금부터는 로그인을 한 것 처럼 작동하게 하기 위한 설정이다

📙application-test

jwt:
  secret: 'testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttest'

spring:
  config:
    activate:
      on-profile: test

  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/testte
    username: 비번
    password: 비번

  jpa:
    database: mysql
    database-platform: org.hibernate.dialect.MySQLDialect
    hibernate:
      ddl-auto: update
    properties:
      hibernate:
        dialect: org.hibernate.dialect.MySQLDialect
        database-platform: org.hibernate.dialect.MySQLDialect
        default_batch_fetch_size: 100

 

나는 TestDB를 MySql로 연결하여 관련 설정을 yml파일에 해두었다

📙WithCustomMockUser

@Retention(RetentionPolicy.RUNTIME)
@WithSecurityContext(factory = WithCustomMockUserSecurityContextFactory.class)
public @interface WithCustomMockUser {

    String id() default "1";
}

📙WithCustomMockUserSecurityContextFactory

@ActiveProfiles("test")
public class WithCustomMockUserSecurityContextFactory implements WithSecurityContextFactory<WithCustomMockUser> {
    @Value("${jwt.secret}")
    String secretKey;

    @Override
    public SecurityContext createSecurityContext(WithCustomMockUser annotation) {
        final SecurityContext securityContext = SecurityContextHolder.createEmptyContext();

        Claims claims;
        UserDetails principal;
        Collection<? extends GrantedAuthority> authorities;

        long now = (new Date()).getTime();
        long acessTokenExpireTime = 1000 * 60 * 30;
        Date accessTokenExpiresIn = new Date(now + acessTokenExpireTime);

        byte[] keyBytes = Decoders.BASE64.decode(secretKey);
        Key key = Keys.hmacShaKeyFor(keyBytes);

        String accessToken = Jwts.builder()
                .setSubject(annotation.id())
                .claim("provider", "KAKAO")
                .claim("nickname", "하나")
                .claim("auth", "ROLE_USER")
                .setExpiration(accessTokenExpiresIn) 
                .signWith(key, SignatureAlgorithm.HS512)
                .compact();

        claims = Jwts.parserBuilder()
                .setSigningKey(key)
                .build()
                .parseClaimsJws(accessToken)
                .getBody();
        authorities = Arrays.stream(claims.get("auth").toString().split(","))
                .map(SimpleGrantedAuthority::new)
                .toList();
        principal = new User(claims.getSubject(), "", authorities);

        final UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
                principal, "", authorities
        );
        securityContext.setAuthentication(authenticationToken);
        return securityContext;

    }
}

📙ScreeningControllerTest

@ApiIntegrateSpringBootTest
@AutoConfigureMockMvc
class ScreeningControllerTest {
    @LocalServerPort
    protected int port;

    @Autowired
    MockMvc mvc;
    @MockBean
    protected ScreeningAdaptor screeningAdaptor;
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss");
    @Autowired
    private ObjectMapper objectMapper;


    @DisplayName("상영회 만들기 시나리오")
    @Test
    //이 WithCustomMockUser가 없으면 unAuthenticated 오류가 난다
    @WithCustomMockUser
    void createScreening() throws Exception {
        PostScreeningRequest request = PostScreeningRequest.builder()
                .posterImgUrl("https://jgjfhdjghsdkjhgkjd")
                .screeningTitle("홍익대학교 졸업전시회")
                .hostName("이한비")
                .category(Category.GRADUATE)
                .screeningStartDate(LocalDateTime.parse("2024-03-20T07:52:06", formatter))
                .screeningEndDate(LocalDateTime.parse("2024-03-20T07:52:06", formatter))
                .screeningStartTime(LocalDateTime.parse("2024-03-20T07:52:06", formatter))
                .location("홍익대학교")
                .information("졸업 작품보러오세요")
                .formUrl("https://sdhgfhsdjkfsjjgsh.com")
                .hostPhoneNumber("010-0000-0000")
                .hostEmail("email1111@email.com")
                .hasAgreed(true)
                .build();

        //When
        mvc.perform(MockMvcRequestBuilders.post("/screening/upload-screening")
                        .with(csrf())
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(request)))
                //Then
                .andExpect(MockMvcResultMatchers.status().isOk());
}

 

 

📙결과

 

원하는 테스트 용 request가 DB에 저장이 잘 됐는지 확인해보면

저장 된 것을 확인 할 수 있다