스프링부트 테스트하기
테스트
@RequestMapping 애너테이션을 무시하고 보면 이 메서드는 일반적인 자바 메서드다. ReadingListRepository의 목(Mock) 구현체를 제공한 후 addToReadingLisrt() 메서드를 직접 호출하여 반환 값을 검증하고 리포지토리의 save() 메서드 호출을 확인하는 테스트는 만들기가 그리 어렵지 않다.
하지만 이 테스트의 문제는 메서드 자체만 테스트한다는 것이다. 테스트를 아예 안 하는 것보다는 낫지만, /로 들어오는 POST 요청을 처리하는 부분은 테스트할 수 없다. 폼 필드들을 Book 매개변수에 제대로 연결 했는지도 테스트할 수 없다. 게다가 메서드가 반환한 String이 특정 값을 포함하는지 검증할 수는 있어도 메서드 처리를 완료한 후 요청을 /로 리다이렉트 했는지 명확히 테스트할 수는 없다.
웹 애플리케이션을 올바르게 테스트하려면 웹 애플리케이션에 실제 HTTP 요청을 보내고 애플리케이션이 요청을 제대로 처리했는지 검증할 방법이 있어야 한다. 다행히 스프링 부트 애플리케이션 개발자에게는 웹 애플리케이션을 테스트할 수 있는 두 가지 옵션이 있다.
스프링 부트 애플리케이션 테스트 옵션
스프링 Mock MVC : 애플리케이션 서버를 구동하지 않고도 서블릿 컨테이너와 거의 비슷하게 작동하는 목 구현체로 컨트롤러를 테스트할 수 있다.
웹 통합 테스트 : 톰캣, 제티 등 내장 서블릿 컨테이너에서 애플리케이션을 실행하여 실제 애플리케이션 서버에서 애플리케이션을 테스트할 수 있다.
Mock MVC를 설정하려면 MockMvcBuilders를 사용한다. 이 클래스는 정적 메서드 두 개를 제공한다.
standaloneSetup() : 수동으로 생성하고 구성한 컨트롤러 한 개 이상을 서비스할 Mock MVC를 만든다.
webAppContextSetup() : 구성된 컨트롤러 한 개 이상을 포함하는 스프링 애플리케이션 컨텍스트를 사용하여 Mock MVC를 만든다.
이 두 옵션의 가장 큰 차이는 standaloneSetup() 메서드는 테스트할 컨트롤러를 수동으로 초기화하고 주입하기를 기대하는 반면, webAppContextSetup() 메서드는 (스프링이 로드한) WebApplicationContext의 인스턴스로 작동한다는 점이다.standaloneSetup() 메서드는 한 컨트롤러에 집중하여 테스트하는 용도로만 사용한다는 점에서 유닛 테스트와 유사하다. 반면에 webAppContextSetup() 메서드는 스프링이 컨트롤하는 물론 의존성까지 로드하여 완전한 통합 테스트를 할 수 있게 한다.
@WebAppConfiguration 애너테이션은 SpringJUnit4ClassRunner가 애플리케이션 컨텍스트로 (기본값인 비웹용 ApplicationContext가 아니라) WebApplicationContext를 생성하도록 선언하다.
setupMockMvc() 메서드는 Junit의 @Before 애너테이션을 붙여 다른 테스트 메서드보다 먼저 실행해야 함을 나타낸다. 이 메서드는 주입된 WebApplicationContext를 webAppContetSetup()메서드에 전달한 후 build() 메서드를 호출하여 MockMvc 인스턴스를 생성하고, 테스트 메서드에서 사용할 인스턴스 변수에 할당한다.
책 정보를 등록할 때는 웹 브라우저가 애플리케이션에 전송할 때 사용하는 application/x-www-form-urlencoded(MediaType.APPLICATION_FORM_URLENCODED)로 콘텐트 타입을 설정해야 한다. 다음으로 MockMvcRequestBuilders의 param() 메서드로 전송할 폼을 시뮬레이션하는 필드들을 설정한다. 요청을 실행하면 응답이 /로 리다이렉트되는지 검증한다. 첫 번째 테스트 메서드가 성공했다면 두 번째 부분으로 넘어간다. 먼저 응답으로 기대하는 값을 담은 Book 객체를 생성하다. 이 객체는 메인 페이지를 요청한 후 반환되는 모델 값과 비교하는 데 사용한다. 이제 /에 GET 요청을 수행한다.모델에는 빈 컬렉션 대신 객체 하나를 담은 컬렉션이 있고, 그 객체가 방금 생성한 Book 객체와 동일한지 확인한다. 두 객체가 같으면 컨트롤러는 POST 요청으로 책을 저장하는 작업을 수행한다고 볼 수 있다. 지금까지는 보안이 없는 애플리케이션을 테스트한다고 가정하고 진행했다. 이제부터는 보안을 적용한 애플리케이션을 테스트하고 싶다면 어떻게 해야 할까?
웹 보안 테스트
테스트하기 이전에 dependency를 추가해야 한다. ( Spring-security-test )
springSecurity() 메서드는 Mock MVC용으로 스프링 시큐리티를 활성화하는 Mock MVC 구성자를 반환한다.
그렇다면 어떻게 인증된 요청을 수행할 수 있을까?
@WithMockUser : 지정한 사용자 이름, 패스워드, 권한으로 UserDetails를 생성한 후 보안 컨텍스트를 로드한다.
@WithUserDetails : 지정한 사용자 이름으로 UserDetails 객체를 조회하여 보안 컨텍스트를 로드한다.
@WithMockUser 애너테이션은 일반적인 UserDetails 객체 조회를 건너뛰고 대신 지정된 값으로 UserDetails를 생성한다. 하지만 여기서 수행할 테스트에는 @WithMockUser가 생성하는 일반적인 UserDetails가 아니라 UserDetails를 구현한 Reader가 필요하다.
@Withuserdetails 애너테이션을 사용하여 테스트 메서드가 작동하는 동안 시큐리티 컨텍스트에 craig 사용자를 로드하도록 했다. 모델에 Reader가 포함된다는 사실을 알고 있으므로 테스트에서 추후 모델과 비교할 Reader 객체를 생성했다. 이제 SecurityConfig의 두 번째 configure() 메서드를 조금 수정해야 한다.
테스트를 실행하면 스프링 시큐리티는 사용자 정보를 조회하는 사용자 상세 서비스를 요구한다. 따라서 사용자 상세 서비스를 테스트 러너에서 참조할 수 있도록 빈을 만들어야 한다.
Last updated