기존 테스트에 시큐리티를 적용하면 시큐리티 옵션이 활성화되어 인증된 사용자만 API를 호출 할 수 있습니다.
그렇기 때문에 인증 권한을 받지 못한 기존 API테스트 코드들을 인증받은 사용자가 호출 한 것처럼 수정을 해야합니다.
src/main 과 src/test 는 서로 다른 환경구성을 가지기 때문에 application-oauth.properties의 설정값을 추가했어도 테스트시에는 application.properties의 설정값까지만 가져오게됩니다.
test에 application.properties가 따로 없으면 mian의 application.properties를 사용합니다.
이 문제를 해결하기 위해 테스트 환경을 위해 test아래에 application.properties를 등록하겠습니다.
src/test/resources/application.properties
spring.jpa.show_sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
spring.h2.console.enabled=true
spring.profiles.include=oauth
spring.session.store-type=jdbc
# Test OAuth
spring.security.oauth2.client.registration.google.client-id=test
spring.security.oauth2.client.registration.google.client-secret=test
spring.security.oauth2.client.registration.google.scope=profile,email
구글 연동은 실제로 하지 않을 것이기때문에 위와 같이 가짜 설정값을 입력했습니다.
스프링 시큐리티 설정때문에 인증되지 않은 사용자의 요청은 이동시키기 때문에 임의로 인증된 사용자를 추가하겠습니다.
build.gradle에 spring-security-test를 추가하겠습니다.
testImplementation("org.springframework.security:spring-security-test")
임의의 사용자 인증을 추가하겠습니다.
PostsApiContollerTest.java
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) // 랜덤포트실행
public class PostsApiControllerTest {
@LocalServerPort
private int port;
@Autowired
private TestRestTemplate restTemplate; // HTTP를 사용하여 통신하는 범용 라이브러리
@Autowired
private PostsRepository postsRepository;
@Autowired
private WebApplicationContext context;
private MockMvc mvc;
@After
public void tearDown() throws Exception {
postsRepository.deleteAll();
}
@Before
public void setup(){ // 테스트가 시작되기 전에 MockMvc 인스턴스생성.
mvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(springSecurity())
.build();
}
@Test
@WithMockUser(roles="USER")
public void Posts가_등록된다() throws Exception {
//given
String title = "테스트 제목입니다.";
String content = "테스트 내용입니다.";
PostsSaveRequestDto requestDto = PostsSaveRequestDto.builder()
.content(content)
.title(title)
.author("작성자")
.build();
String url = "http://localhost:"+ port + "/api/v1/posts";
//when
// mvc를 이용해서 테스트 문자열로 본문을 표시하기위해 Json으로 변환
mvc.perform(post(url)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(new ObjectMapper().writeValueAsString(requestDto)))
.andExpect(status().isOk());
List<Posts> all = postsRepository.findAll();
assertThat(all.get(0).getTitle()).isEqualTo(title);
assertThat(all.get(0).getContent()).isEqualTo(content);
}
@Test
@WithMockUser(roles="USER")
public void Posts가_수정된다() throws Exception {
//given 수정할 게시글 작성
Posts savePosts = postsRepository.save(Posts.builder()
.title("수정테스트 제목")
.content("수정테스트 내용")
.author("수정테스트 작성자")
.build());
Long updateId = savePosts.getId();// 작성한 게시글에서 아이디 가져오기
String expectTitle = "새 제목"; // 수정할 제목
String expectContent = "새 내용"; // 수정할 내용
PostsUpdateRequestDto requestDto = PostsUpdateRequestDto.builder()
.title(expectTitle)
.content(expectContent)
.build();
String url = "http://localhost:" + port + "/api/v1/posts/" + updateId;
HttpEntity<PostsUpdateRequestDto> requestEntity = new HttpEntity<>(requestDto);
//when
mvc.perform(post(url)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(new ObjectMapper().writeValueAsString(requestDto)))
.andExpect(status().isOk());
//then
List<Posts> all = postsRepository.findAll();
assertThat(all.get(0).getTitle()).isEqualTo(expectTitle);
assertThat(all.get(0).getContent()).isEqualTo(expectContent);
}
}
@WithMockUser(roles="USER")
는 roles에 권한을 추가해 가짜 사용자를 만들어 사용할 수 있습니다.
@WebMvcTest
는@ControllerAdvice
와@Controller
를 읽으므로@Repository
,@Service
,@Component
는 스캔대상이 아니기 때문에 securityConfig 설정을 읽기위한 서비스는 읽을 수 없습니다.
그래서 securiryConfig를 스캔대상에서 제거하고 @WithMockMvc
를 이용해서 가짜 사용자를 생성하겠습니다.
HelloControllerTest.java
@WebMvcTest(controllers = HelloController.class,
excludeFilters = {
@ComponentScan.Filter( type = FilterType.ASSIGNABLE_TYPE, classes = SecurityConfig.class)
})
public class HelloControllerTest {
...
@WithMockUser(roles="USER")
@Test
public void hello를_리턴한다() throws Exception {
...
}
@WithMockUser(roles="USER")
@Test
public void helloDto가_리턴된다() throws Exception {
...
}
@EnableJpaAuditing
를 사용하기위해선 하나의 엔티티 클래스가 필요하지만 MockMvcTest를 사용하는 경우 엔티티가 없습니다.
그렇기 때문에 @SpringBootApplication
와 @EnableJpaAuditing
를 분리하겠습니다.
Application.java
~~@EnableJpaAuditing ~~삭제
@SpringBootApplication
public class Application {
public static void main(String[] args){
SpringApplication.run(Application.class, args);
}
}
config패키지에 JpaConfig클래스
를 새로 만들어 @EnableJpaAuditing
를 추가했습니다.
@Configuration
@EnableJpaAuditing // JPA Auditing 활성화
public class JpaConfig {}
@WebMvcTest
는 일반적인@Configuration
은 스캔하지 않습니다.