본문 바로가기

스프링

Mock테스트_빈등록코딩_컨트롤러의종류와어댑터

1.     Com.springsource.javax.activation : Spring java-mail

2.     Com.springsource.javax.mail : Spring java-mail

3.     Com.springsource.org.aopalliance : Spring ProxyFactoryBean

4.     Com.springsource.org.aspectj.tools : AspectJExpressionPointcut 포인트컷 표현식 지원

5.     Com.springsource.junit : junit

6.     Commons-logging : Spring-context가 사용

7.     Mail : java-mail

8.     Mockito : 목 프레임워크 중 Mockito

9.     Mysql-connector : Mysql JDBC

10.   Org.springframework.context.support : Spring java-mail

11.   Spring-aop : 스프링 기능 자체의 aop, Spring ProxyFactoryBean

12.   Spring-bean : 스프링 코어와 함께 의존성 주입 제공 (Core Container)

13.   Spring-context : 스프링 코어, BeanFactory를 확장한 어플리케이션 컨텍스트 구현, 리소스 로드 및 국제화 지원(Core Container)

14.   Spring-core : 다른 스프링 모듈이 사용하는 유틸리티(Core Container)

15.   Spring-expression : EL 확장 Bean속성(배열, 컬렉션 포함).(Core Container)

16.   Spring-jdbc : 스프링이 지원하는 jdbc.

17.   Spring-test.jar

-       @RunWith : Junit 프레임워크의 테스트 실행방법을 확장시 사용.

-       SpringJUnit4ClassRunner : 어플리케이션컨텍스트를 만들고 관리하는 확장 클래스

-       @ContextConfiguration(경로) : 자동으로 만들어줄 어플리케이션 컨텍스트 설정파일

18.   Spring-tx : DuplicateKeyException.class 파일 존재 및 스프링 트랜잭션

19.   Spring-web : ContextLoaderListener 내장 루트 컨텍스트(서비스,DAO)

20.   Spring-webmvc : SpringMVC를 위한 리졸버, 모델앤뷰 등 지원

 

 

10,11. 원래 /hello?name=Spring과 같은 URL을 넣어야하지만 MockRequest는 파라미터를 URL에 넣는 대신 파라미터 셋팅

16. 레이아웃정보는 검증대상이 아니기 때문에 위와같이 테스트가 가능하다.

> 위처럼 일반 서블릿을 테스트한닫면 서블릿의 오브젝트를 생성하고, 기껏해야 init() 메소드를 호출해주는 정도 외에는 별다른 준비할 필요가 없다. 하지만 스프링의 DispatcherServlet은 다르다.

 

MockHttpServletRequest의 테스트용으로 유용하게 쓸 수 있는 메소드.

 

Request, response 외에도 세션, 서블릿컨텍스트, 서블릿콘피그 등 다양한 테스트 도우미들이 많이 있다.

스프링의 MockJSP로 포워딩하는 경우 JSP 서블릿을 호출해 그 결과를 전부 가져오는 것은 불가능하다. 데이터를 검증하는데 주로 쓰임.

 

* 디스패처 서블릿은 초기화 할 수 있도록 ~~servlet.xml을 가진 설정파일을 준비해줘야하고, 매번 전략을 다르게 구성하기 위해 설정을 다르게 준비하는 일만해도 간단하지 않다. 아래설명은 디스패처 서블릿을 확장해서 간단하게 테스트하기 위한 확장 클래스이다.

 

34. … : Class타입의 파라미터가 몇 개이든지 상관없이 다받겠다는 의미.

44. classPackageAsResourcePath : 주어진 파라미터 클래스(clazz)의 패키지 위치를 리턴받음.

 

68, 70 : 디스패처서블릿 컨텍스트를 생성하는 메소드를 오버라이드 해서 메타정보를 테스트용 메타정보를 이용해서 서블릿 컨텍스트를 생성하게 함.

 

 

 

이제 Junit을 이용해서 단위 테스트를 만들어 테스트해보자.

 

원래 HelloSpring은 루트 컨텍스트에 따로 등록했지만 동작을 확인하는 것이 현재 주 관심사이므로 따로 분리하지 않고 컨텍스트를 만들었다.

 

16. 디스패처서블릿을 상속하여 만든 서블릿을 생성.

17. setRelativeLocation : setLocations을 사용하여 클래스패스를 전부 적어줘도 되지만, 현재 클래스부터 상대적인 위치를 지정가능. 첫번째 파라미터는 Class타입 오브젝트인데, 동일 패키지라면 getClass()로 테스트 클래스 자신을 넣어주면된다. 여기선 하나의 설정만 등록했지만, 여러개를 등록할 수도 있다.

18. 빈으로 등록할 클래스역시 여러 개 셋팅이 가능하다.

19. 서블릿 컨텍스트가 사용할 설정정보를 모두 넣었으면, 다음은 ServletConfig 오브젝트를 만들어 서블릿을 초기화해줘야 한다. 스프링이 제공하는 MockServletConfig를 사용하면된다. 초기화하지 않고 사용하려고 하면 예외가 발생함.

25. JSP뷰를 HTML을 완성해야 최종 생성 결과를 확인할 수 있다. 하지만 복잡한 작업인데다가 굳이 그럴 필요없으므로 적절한 모델과 뷰를 리턴했는지를 직접 검증하는 것이 훨씬 간편하고 효율적이다.

* 서버를 돌릴 필요없이 간단하게 테스트가 마무리된다.

 

 

DispatcherServlet 전략과 상관없이 컨트롤러 클래스의 로직을 테스트하는게 목적이라면 디스패처를 거치지 않고 컨트롤러에 바로 요청을 보내서 검증하는 방법이 낫다.

> 하지만 언젠가는 HTTP 요청처리와 MVC 구성 전략까지 검증이 가능한 DispatcherServlet 테스트도 필요할 것이다. 위처럼 스프링 컨텍스트를 만들지 않고 HelloContorller 오브젝트를 직접 생성해서 단위테스트를 할 수도 있다.

 

> 하지만 테스트하는데도 준비할게 많고 테스트 코드가 길어진다. 아래 커스터마이징.

 

테스트를 돕기위한 인터페이스. 스프링이 아닌 다른 환경에서 테스트가 있을 수 있으므로 인터페이스

 

사용법은 아래 테스트를 참고 > XML 설정없이 컨트롤러가 동작

58. initRequest : URIget or post방식이 넘어옴

 

74. addParameter : mockRequest에 파라미터 셋팅하여 자기자신 리턴

89. ServletConfig 오브젝트를 만들어 서블릿을 초기화해줘야 한다. 스프링이 제공하는 MockServletConfig를 사용하면된다. 초기화하지 않고 사용하려고 하면 예외가 발생함.

114. getContext : 때로는 디스패처 서블릿 실행 후 컨텍스트의 상태나 빈 오브젝트의 변화를 확인해 볼 필요가 있다. 이때 서블릿 컨텍스트를 가져와 사용.

120. getBean : 서블릿 컨텍스트로부터 빈을 가져오는게 목적이라면 getContext() 대신 getBean()을 사용한다.

127. getModelAndView : 가장 많이 사용되는 것. 모델 오브젝트와 뷰 이름을 가져와 검증.

130. assertModel : 모델 오브젝트만 검증시 사용

 

135. assertViewName : 뷰이름만 검증시 사용

141. getContentAsString : assertThat(..getContentAsString(), is(“{\”name\”:\”Spring\”}”); 의 형태로 JSP외의 결과를 확인할 수 있다.

 

16. AbstractDispatcherServletTest를 상속해서 테스트를 만들어야한다.

20. setRelativeLocation : setLocations을 사용하여 클래스패스를 전부 적어줘도 되지만, 현재 클래스부터 상대적인 위치를 지정가능. 여기선 하나의 설정만 등록했지만, 여러개를 등록할 수도 있다.

23. HTTP메소드를 ‘GET’과 같이 문자열로 넣어도 되지만 안전하게 RequestMthod 이늄을 이용해 지정해주는 편이 낫다. HTTP 메소드가 GET 인 경우는 생략 가능하다.

 

* 컨트롤러의 역할

> 사용자가 바르게 요청했는지 검증.(누락항목, 바른형식)

> 서비스계층에 요청해서 DB의 정보와 비교 검증 및 예외처리

> 멀티파트 등 바이너리 정보가 전달됐을 경우 파일로 저장 등의 적절한 타입변환 및 가공

> 뷰 종류(jsp,json) 선택 및 리다이렉트

> 상태값 세션 저장 및 제거

* 이들을 세분화해 줄 필요가 있다 : 스프링 MVC가 지원하는 컨트롤러 4가지

1) ServletSimpleServletHandlerAdapter

> web.xml에 서블릿을 등록하지 말고 스프링 MVC 컨트롤러를 등록해서 사용하는게 좋다.

> 서블릿 클래스 코드를 그대로 유지하면서 스프링 빈으로 등록된다.(비즈니스로직, DAO DI방식으로 사용이 가능해진다.)

> , 빈으로 컨트롤러를 등록한 경우 init(), destroy()와 같은 생명주기 메소드가 호출되지 않는다. 초기화 작업을 하려면 <bean> 태그의 init-method 애트리뷰트나 @PostConstruct 애노테이션 등을 이용해 빈 생성 후에 초기화 메소드가 실행되게 해야한다.

 

23. HelloServlet29라인에 내부클래스와 SimpleServletHandlerAdapter 등록. 원래는 아래스샷처럼 등록해야줘야함.

AbstractDispatcherServletTest 덕분에 XML 설정 파일 없이 테스트할 수 있다.

25. Servlet 타입의 컨트롤러는 모델과 뷰를 리턴하지 않는다. 또한, DispatcherServlet은 모델앤뷰 타입의 오브젝트 대신 null을 리턴하면 뷰를 호출하는 과정을 생략하고 뷰 호출을 하지 않는다. 서블릿 컨트롤러처럼 직접 HttpServletResponse에 결과를 넣는 컨트롤러도 있기 때문에 위와같이 테스트함.

28. 디폴트인 빈 이름을 이용한 핸들러 매핑 전략을 사용할 수 있도록 @Component 태그를 이용해 빈 이름으로 URL을 넣어줌. 빈스캐너의 자동인식을 위해서가 아닌 단지 이름을 부여해주기 위해 사용함. @Component 대신 @Named 애노테이션을 사용해됨.

 

이렇게 빈으로 등록되어 있으면 DispatcherServlet은 이를 자동으로 감지해 사용한다. 또한 핸들러 어댑터는 컨트롤러 종류마다 하나씩 필요한데 동시에 두 가지 이상의 컨트롤러를 사용할 수 있기 때문에 여러 개의 핸들러 어댑터를 사용하기도 한다.

> DispatcherServlet은 여러 개의 핸들러 매핑에 의해 사용할 컨트롤러 빈을 찾아주면 그에 맞는 핸들러 어댑터를 이용해 컨트롤러를 호출해주는 것이다.

> AbstractDispatcherServletTestXML 설정파일이 없이도 빈 클래스를 직접 제공해주는 방식으로 서블릿 컨텍스트에 빈을 등록할 수 있었다.

 

서블릿 인터페이스와 비슷하다. 실제로 HttpRequestHandler는 서블릿처럼 동작하는 컨트롤러를 만들기 위해 사용한다. 전형적인 서블릿 스펙을 준수할 필요없이 HTTP 프로토콜을 기반으로 한 전용 서비스를 만들려고 할 때 사용할 수 있다.

> 모델과 뷰 개념이 없는 HTTP 기반의 RMI(Remote Method Invocation)와 같은 로우레벨 서비스를 개발할 때 이용하는 디폴트 전략이다. Handler에 대한 빈 등록이 없으면 이것을 사용하게됨.

 

* ControllerSimpleControllerHandlerAdapter

Controller 컨트롤러는 DispatcherServlet이 컨트롤러와 주고받는 정보를 그대로 메소드의 파라미터와 리턴 값으로 갖고 있다. 애노테이션을 이용한 컨트롤러가 본격적으로 등장하기 전까지 많이 사용되던 컨트롤러.

> Controller 인터페이스를 구현하기만 하면 되기 때문에, 여타 MVC 프레임워크의 컨트롤러보다 유연하게 컨트롤러 클래스를 설계할 수 있다는 장점이 있다.

> 하지만 권장하지 않는다. 웹브라우저를 클라이언트로 갖는 컨트롤러로서의 필수 기능이 구현되어 있는 AbstractController를 상속해서 컨트롤러를 만드는게 편리하기 때문이다. 물론 AbstractController를 상속해서 컨트롤러를 만드는게 편리하기 때문. 물론 AbstractControllerController인터페이스를 구현한 Controller를 구현한 Controller타입의 컨트롤러이다.

 

AbsractController는 위와 같은 웹개발에 유용하게 쓸 수 있는 프로퍼티를 제공해준다. API문서 참고.

> Controller 타입 컨트롤러는 스프링 MVC를 확장해서 애플리케이션에 최적화된 전용 컨트롤러를 설계할 때 가장 유용하다. 컨트롤러 클래스를 만들고 이를 상속받아서 애플리케이션의 컨트롤러를 만들게 하면 된다.

> 상속받는 것이 불편하게 느껴진다면, 전용 컨트롤러 인터페이스를 정의하고 핸들러 어댑터를 만드는 방법도 가능하다.

 

12. 뷰 이름을 프로퍼티로 설정해놨기 때문에 코드 수정없이 XML 설정에서 뷰를 변경 가능.

19. Controller 인터페이스의 handleRequest()

> 공통적인 작업을 수행하도록 해야하므로 개별 컨트롤러를 만들 때 이를 직접 사용하면 안됨.

> 클라이언트가 컨트롤러를 찾으면 handleRequest 메소드가 호출됨.

23~34. Request를 추출해서 이를 별도의 맵에 담아서 파라미터를 전달하고, Map타입의 모델을 미리 생성해서 서브컨트롤러에서 미리 만들어진 모델에 담기만 하면 코드가 단순화된다.

33. 이라인에서 서브컨트롤러가 구현할 메서드를 호출한다.

38. 개별 컨트롤러에서 구현해줘야 하므로 추상 메소드로 정의한다.

* 위처럼 handlerRequest()를 직접 사용하는게 아니라 확장 등 활용할 줄 알아야한다.

 

컨트롤러가 특정 클래스를 상속하는데 문제가 없다면, Cotroller인터페이스를 구현한 컨트롤러 클래스를 상속 받아서 애플리케이션의 컨트롤러를 만들게 하면 된다. 상속받는 것이 불편하게 느껴진다면, 전용 컨트롤러 인터페이스를 정의하고 핸들러 어댑터를 만드는 방법도 가능하다.

> 웹환경에 종속적인 HttpServletRequest 같은 건 전혀 보이지 않아서 좋다.

 

Controller처럼 로우레벨의 HttpServletRequest를 받는 것은 불편하다.

 

 

 

AnnotaionMethodHandlerAdapter 역시 DispatcherServlet의 기본 핸들러 어댑터 전략이므로 따로 설정해줄 필요는 없다.