코딩블로그
멀티 모듈 환경에서 내가 만든 api 테스트해보기 Feat. MockMvc, profileResolver, WithCustomUser 본문
PopcornMate
멀티 모듈 환경에서 내가 만든 api 테스트해보기 Feat. MockMvc, profileResolver, WithCustomUser
_hanbxx_ 2024. 3. 24. 16:52728x90
내가 작성한 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에 저장이 잘 됐는지 확인해보면
저장 된 것을 확인 할 수 있다
728x90
'PopcornMate' 카테고리의 다른 글
[Spring]Junit5 Controller 단위 테스트 @AutoConfigureMockMvc, @MockBean, @MockMvc 트러블슈팅 (0) | 2024.04.06 |
---|---|
[Spring] 프로젝트 협업 툴 디스코드 연동해서 에러 메세지 받아보기 (Feat. feign, logback.xml) (1) | 2024.04.03 |
[Spring] FCM 구축하기 & FCM과 @Scheduled을 이용하여 특정 시간대에 알림 보내기 (4) | 2024.03.11 |
[Spring] Amazons S3 Presigned Url 구현하기 (0) | 2024.03.11 |
[리팩토링]InteliJ를 활용하여 멀티모듈 패키지 의존성 사이클 검사 및 개선 (1) | 2024.02.16 |