웹 관련 기술을 테스트하는 것은 어렵다. 컨테이너에 올리고 수동테스트 하는 건 어렵지 않지만 컨테이너 밖에서 자동화된 단위/통합 테스트를 구성하는 것은 쉽지 않다. 하지만 불가능한 것은 아니다.

스프링의 웹 기술은 DispatcherServlet과 그 안에서 생성되는 WebApplicationContext가 기반이 된다. SpringMVC/@MVC를 쓰든 아답터를 이용해서 다른 웹 프레임워크가 연동되든 마찬가지다.

@MVC는 @RequestMapping을 이용해서 URL과 핸들러-메소드를 연결한다. Controller처럼 인터페이스가 정해져 있는 것은 아니고 메소드 시그니처와 파라미터의 조합을 통해서 매우 유연하게 웹 요청을 핸들링 하는 메소드를 작성할 수 있다. 파라미터, 리턴타입, 애노테이션의 조합에 따라 매우 다양한 방식으로 동작하기 때문에 그 경우를 모두 따져서 테스트 해보면서 익히지 않으면 제한적으로 밖에 사용할 수 없다. 문제는 이를 컨테이너에서 테스트 하는 것은 매우 번거롭다는 것이고 자동화 하기 힘들다는 것이다.

대신 컨테이너 밖에서 웹에서 동작하는 것처럼 테스트를 만들 수 있다면 다양한 테스트를 만들어서 @MVC가 동작하는 방식을 학습할 수 있다. request, session 스코프의 경우도 마찬가지이다. 이 스코프들이 동작하는 방식을 테스트 하는 것은 단순한 @MVC 매핑이나 MAV 기능을 테스트 하는 것보다 훨씬 더 복잡하다. 여러번의 요청과 여러 세션을 바꿔가면서 테스트를 해야 한다. 이것은 수동 테스트로도 매우 귀찮은 작업이다. 역시 자동화된 컨테이너 밖에서 동작하는 테스트를 만들 수 있다면 좋을 것이다.

HttpServletRequset와 Response를 이용해서 웹에서 동작하는 SpringMVC를 테스트 하려면 DispatcherServlet을 이용해야 한다. DispatcherServlet은 configClass 정보를 이용해서 자동으로 웹 컨텍스트를 만들고 지정된 설정파일을 읽어서 초기화 한다. 설정파일을 따로 만들어두고 DispatcherServlet을 직접 DI해주고 스프링이 제공하는 웹용 Mock 오브젝트를 사용하면 이런 스프링 웹 테스트를 만드는 것이 어렵지 않다.

하지만 오늘 도전해볼 것은 이전에 해본 것처럼 XML없이 스태틱 클래스와 애노테이션 설정만을 가지고 웹 테스트를 만드는 것이다.

DispatcherServlet의 빈 설정을 코드로 제어하고 싶다면 DispatcherServlet의 createWebApplicationContext() 메소드를 오버라이드 해서 직접 컨테스트를 만들게 하면 된다. 여기서 만들어지는 컨텍스트는 WebApplicationContext류여야 한다. 코드에 의해서 빈을 등록하게 할 수 있는 WAC는 StaticWebApplicationContext가 있다.

문제는 SWAC는 registerBeanDefinition()을 이용해서 빈 클래스를 등록하게 할 수는 있지만 기본적으로 애노테이션 설정을 적용하기 위해서 필요한 다양한 후처리기들이 내장되어 있지 않다. 따라서 @Autowired니 @Scope같은 것들이 적용되지 않는다. 5개에 달하는 <context:annotation-config/>에 의해서 등록되는 애노테이션 설정을 위한 후처리기를 수동으로 등록해주지 않으면 안된다. 매우 번거로운 일이다.

다음으로 고려해볼 WAC는 AnnotationConfigApplicationContext의 웹 버전이라고 할 수 있는 AnnotationConfigWebApplicationContext이다. 문제는 ACAC와 달리 ACWAC는 등록할 클래스를 직접 넣어주는 방법을 제공하지 않고 빈 스캐닝을 위한 폴더만 지정할 수 있다. ACAC와 ACWAC가 다른 사람이 개발한게 아닌가 하는 의심이 들정도로 제공하는 기능이 다르다. 물론 ACAC는 코드에 의해서 테스트 하는데 사용되는 것이 주 용도이고, ACWAC는 실전용이니 그럴 수도 있겠다 싶지만.

그래서 ACWAC를 사용하면서 XML없이, 스태틱 클래스로 만든 테스트용 빈을 추가할 방법은 없다. 스캐닝용 폴더를 따로 만들고 그 안에 빈 클래스를 넣지 않으면 안된다. 귀찮은 일이다.

그래서 ACWAC를 대신해서 AbstractRefreshableWebApplicationContext를 상속해서 애노테이션 리더를 이용해서 빈 클래스를 직접 등록할 수 있도록 만들어줄 필요가 있다. 이를 DispatcherServlet의 기본 컨텍스트 생성 메소드를 오버라이드해서 넣는다면 애노테이션 방식의 클래스를 바로 지정해서 빈으로 등록하고 DispatcherServlet을 테스트 할 수 있는 테스트용 DispatcherServlet을 만들 수 있다.

이렇게 만든 게 다음 클래스이다.

public class AnnotationConfigDispatcherServlet extends DispatcherServlet {
    private Class<?>[] classes;
    public AnnotationConfigDispatcherServlet(Class<?> …classes) {
        super();
        this.classes = classes;
    }
    protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
        AbstractRefreshableWebApplicationContext wac = new AbstractRefreshableWebApplicationContext() {
            protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
                    throws BeansException, IOException {
                AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(beanFactory);
                reader.register(classes);
            }
        };

        wac.setServletContext(getServletContext());
        wac.setServletConfig(getServletConfig());
        wac.refresh();
        return wac;
    }
}

그리고 다음은 이를 이용해서 request scope빈이 매 request마다 하나씩만 만들어져서 사용되는 것을 테스트 해보려고 만든 코드이다. 내부에서 스코프가 어떻게 처리되는지 알 필요 없이 웹 기반의 스코프를 테스트 하는 코드를 이렇게 간단히 만들 수 있으니 매우 만족스럽다. session scope를 테스트 해보는 것도 어렵지 않다. Mock request의 session을 유지 또는 변경해가면서 scoped 빈의 변화를 살펴보면 된다. 여기서는 Provider를 사용했는데 Proxyed Scope를 이용하는 코드를 테스트 하는 것도 간단하다. @Scope의 proxyMode를 default에서 interface나 targetClass로 바꿔주고 Provider로 DL하는 대신 proxyed scope로 DI하게 바꾸면 된다. @RequestMapping의 테스트도 쉽게 만들 수 있다. 심심할 때 해보자.

MockHttpServletResponse response = new MockHttpServletResponse();
@Test
public void requestScope() throws ServletException, IOException {
    MockServletConfig ctx = new MockServletConfig();
    DispatcherServlet ds = new AnnotationConfigDispatcherServlet(HelloController.class, HelloService.class, RequestBean.class, BeanCounter.class);
    ds.init(new MockServletConfig());
    BeanCounter counter = ds.getWebApplicationContext().getBean(BeanCounter.class);
    ds.service(new MockHttpServletRequest("GET", "/hello"), this.response);
    assertThat(counter.addCounter, is(2));
    assertThat(counter.size(), is(1));
    ds.service(new MockHttpServletRequest("GET", "/hello"), this.response);
    assertThat(counter.addCounter, is(4));
    assertThat(counter.size(), is(2));
}
@RequestMapping("/") static class HelloController {
    @Autowired HelloService helloService;
    @Autowired Provider<RequestBean> requestBeanProvider;
    @Autowired BeanCounter beanCounter;
    @RequestMapping("hello") public String hello() {
        beanCounter.addCounter++;
        beanCounter.add(requestBeanProvider.get());
        helloService.hello();
        return "";
    }
}
static class HelloService {
    @Autowired Provider<RequestBean> requestBeanProvider;
    @Autowired BeanCounter beanCounter;
    public void hello() {
        beanCounter.addCounter++;
        beanCounter.add(requestBeanProvider.get());
    }
}
@Scope("request") static class RequestBean {}
static class BeanCounter extends HashSet { int addCounter = 0; };

Related posts:

  1. Inside Spring (5) PropertyPlaceholderConfigurer를 @Bean으로 정의해서는 안되는 이유
  2. Spring 상식퀴즈 (1) – DI 태클하기
  3. Spring 3.0 (56) @Bean 사용의 주의사항
  4. Spring 3.0 (58) Static Class를 XML없이 빈으로 등록하기
  5. Spring 3.0 (52) 반쪽짜리 3.0 RC1 공개
  6. S1A 2008 셋째날 – Spring JavaConfig
  7. Spring 3.0 EL (Spel)을 이용한 AssertThrows 확장 (1)
  8. Spring 3.0 (26) Spring Expression Language와 @Value
  9. Spring 3.0 EL (Spel)을 이용한 AssertThrows 확장 (2)
  10. [토스3] 테스트를 위한 필드 주입 유틸
  11. Spring 3.0 (60) 클래스패스 리소스를 지정할 때 주의사항과 팁
  12. Spring 3.0 (46) Spring 3.0 M4 릴리스
  13. Spring 3.0 (54) 드디어 등장한 ConfigurationClassApplicationContext
  14. 미리 보는 Spring 3.0.1의 변경사항
  15. Spring 3.0 (2) R-518 스프링의 새 모듈 OXM(Object/XML Mapping)

Facebook comments:

to “Inside Spring (6) 애노테이션 설정 지원 스프링 웹 테스트용 DispatcherServlet 만들기”

  1. 포인트컷 테스트 만큼이나 멋진 테스트로군요. 캬~~
    한 요청 내에서 분명 두번 add 했지만 request 스콥인데다 Set이니까 인스턴스 하나만 들어갔고
    두 번째 요청에서도 두번 add해서 총 4번 add 했지만 이번에 새로 만들어진 RequestBean을 하나 추가했으니 총 2개..

    초간단 해설이었습니다.ㅋㅋ

  2. 이미 보셨을지도 모르겠습니다만, 엉클 밥이 최근에 DI Framework에 대한 글을 썼네요.
    http://blog.objectmentor.com/articles/2010/01/17/dependency-injection-inversion

    (아… 이거 보고 오해 하시는분 계실까봐 노파심에,
    저글은 DI 자체에 대한 얘기가 아니라, DI Framework의 사용에 대한
    엉클 밥 자신의 의견을 쓴 글입니다).

    저야 DI Framework던 무슨 Framework던 그냥 도구라고
    보기 때문에 도움되고 필요하면 쓰는거고 아니면 마는거고 인데…
    엉클 밥의 들어놓은 예제 상황은 자신의 주장을
    정당화 하기에 좀 많이 부실하지 않나하는 생각이 드는군요.

    이유는,
    1. 어차피 DI Framework를 사용해도 DI 하는 코드는 한곳에 몰아 넣을수 있고,
    2. 엉클 밥이 사용하기 싫다던 @Inject annotation 은 이제 Java 표준에 들어가게 됐으며,
    3. Dependency 안에 다른 Dependency 가 들어가는 상황이나 여러곳에서 사용되는 하나의 Dependency 를 Singleton으로 프로그래밍 하지 않고, inject 할때 항상 같은 instance 를 주입하게 해야하는 경우, 이거 일일이 직접 관리하면 좀더 골치 아프고,
    4. 어플 개발에서 중요한 것은, problem domain 의 문제점을 해결하는것인데, 부수적인 instantiation 문제로 이거 관리하는 코드를 직접 작성하면서, 시간과 노력을 빼앗기고 있을 이유가 없으며,
    5. 결국 엉클 밥이 보여준게 factory를 만들어서 그 안에 DI framework가
    사용되는 코드를 집어넣는건데, DI framework가
    factory 없애고, Singleton 안 만들어도 되게 해줬는데,
    factory를 다시 만들어서 DI Framework 사용 코드를 거기에 넣는다고요?! :O
    정도 되겠군요. 지극히 개인적인 생각입니다…@_@;

    Spring Framework 처럼 framework 의 종합선물세트야 DI Framework 때문이
    아니라도 쓸 이유가 많이 있겠죠.
    저의 경우만 봐도 AOP야 이미 AspectJ 로 하고 있고,
    DI Framework는 Google Guice 를 써도 되지만,
    그럼에도 불구하고 Spring 을 쓰는 이유는,
    많이 있겠지만, 저의 경우 가장 큰 이유는
    JEE server가 아닌 Tomcat 같은 Servlet Container 에서
    JPA에서 쓸 EntityManager 가 알아서 주입되고,
    @Transactional 같은 annotation만 가지고,
    직접 transaction 관리하는 코드 작성할 필요없이
    간편하게 프로그래밍이 가능하다는 점입니다.

    저야 뭐 초보자라… ^^;

    사실은 전문가인 Toby 님께서는 엉클 밥의 의견에 대해
    어떻게 생각하시는지 궁금합니다.
    지금 많이 바쁘실테니, 나중에 혹시라도 시간이 있으시면,
    관련된 얘기나 비슷한 얘기라도
    올려주시면, 좋겠다고 생각했는데,
    이미 DI 와 DI Framework 관련 얘기는 많이 하셔서… ^^;

  3. tnVNiQ qfmpuofxyxri, [url=http://lkdofiwsgdjd.com/]lkdofiwsgdjd[/url], [link=http://obnntnlszhwt.com/]obnntnlszhwt[/link], http://apwknqrmqxza.com/

  4. Today, I went to the beach with my kids. I found a sea shell and gave it to my 4 year old daughter and said “You can hear the ocean if you put this to your ear.” She put the shell to her ear and screamed. There was a hermit crab inside and it pinched her ear. She never wants to go back! LoL I know this is completely off topic but I had to tell someone!

  5. For that reason, you could period the glenohumeral joint secure to cart the idea one particular prolonged neck wrist strap. It is the precious metal shinning CC form, a Valentines bracelets louis vuitton monogram wallet and also the tender lilac color which add some the majority of intrigue to the tote.

  6. Force down closure comes with stunning common box shape around protecting louis vuitton wallet men signature. You can just put your lip stick, money and also mobile phone in it.

  7. The woman louis vuitton Shoes first be employed in put and also mountain songs inside the Nineteen sixties has been overshadowed by means of the girl have a problem with drug use in the 1970s. Throughout the primary two-thirds of this 10 years, together with minor notice, louis vuitton Shoes pair of recording studio pictures have been developed.

  8. An mysterious participator mentioned, “There had been nothing at all poor around the attire. curfew within sudan because 1990 including marriages as well as louis vuitton shoes graduations.

  9. gucci mane jail release date Inside Spring (6) 애노테이션 설정 지원 스프링 웹 테스트용 DispatcherServlet 만들기 » Toby’s Epril

  10. buy gucci tracksuit Inside Spring (6) 애노테이션 설정 지원 스프링 웹 테스트용 DispatcherServlet 만들기 » Toby’s Epril

  11. designer bridal shoes discount Inside Spring (6) 애노테이션 설정 지원 스프링 웹 테스트용 DispatcherServlet 만들기 » Toby’s Epril

  12. I dont know what to say. This blog is fantastic. Thats not really a really huge statement, but its all I could come up with after reading this. You know so much about this subject. So much so that you made me want to learn more about it. Your blog is my stepping stone, my friend. Thanks for the heads up on this subject. <a href=//legosforgirls.info/
    ディーゼル 時計 http://www.idahotherapyservices.com/

  13. great shoes online Inside Spring (6) 애노테이션 설정 지원 스프링 웹 테스트용 DispatcherServlet 만들기 » Toby’s Epril

  14. http://www.yfcx.net/plus/view.php?aid=24164 Inside Spring (6) 애노테이션 설정 지원 스프링 웹 테스트용 DispatcherServlet 만들기 » Toby’s Epril

  15. http://www.vercs.com/forum.php?mod=viewthread&tid=341452 Inside Spring (6) 애노테이션 설정 지원 스프링 웹 테스트용 DispatcherServlet 만들기 » Toby’s Epril

  16. http://www.sccwl.com/bbs/forum.php?mod=viewthread&tid=12969&fromuid=2282 Inside Spring (6) 애노테이션 설정 지원 스프링 웹 테스트용 DispatcherServlet 만들기 » Toby’s Epril

  17. The information youve written on your website is really good, and Im looking for someone to do some content writing for me (Ill pay you a good salary). Please contact me asap.
    グッチ 財布 新作 2013 http://www.quepachuca.com/categories-12.html

  18. What’s Happening i am new to this, I stumbled upon this I’ve discovered It absolutely useful and it has helped me out loads. I’m hoping to contribute & help other users like its helped me. Great job.|
    アグ ブーツ 激安 http://www.vivendadelmar.com/アグ-インソール-c-6.html

  19. comfort shoes Inside Spring (6) 애노테이션 설정 지원 스프링 웹 테스트용 DispatcherServlet 만들기 » Toby’s Epril

  20. Can easily compost, their floor refresher, and you that may assist you unquestionably the floor give the top nutritional vitamins and as well minerals one’s own plants and flowers ought to.

  21. Excellent article! We are linking to this particularly great article on our website. Keep up the great writing.|
    アグ ブーツ レディース http://www.vivendadelmar.com/アグ-イヤーマフ-c-4.html

  22. You would like to desired to conduct is use the directory is important of them in intellectual property civil rights or emblems one of the queue for brand names.

  23. By any means, you’re obtain realize to popularity or perhaps even concerning denial regarding your utility.

  24. This is really interesting, You’re a very skilled blogger. I’ve joined your feed and look forward to seeking more of your great post. Also, I’ve shared your web site in my social networks!|
    UGG ブーツ 人気 http://www.mwhittlephoto.com/

  25. Hello exceptional website! Does running a blog such as this take a lot of work? I have absolutely no expertise in programming but I was hoping to start my own blog soon. Anyhow, if you have any recommendations or tips for new blog owners please share. I understand this is off subject however I simply needed to ask. Thanks!|
    アグ ブーツ 正規品 http://www.mwhittlephoto.com/

  26. I see a lot of interesting content on your website.
    You have to spend a lot of time writing, i know how to save
    you a lot of time, there is a tool that creates high quality, SEO friendly articles in couple of
    seconds, just type in google – k2 unlimited content

Leave a Reply

(required)

(required)

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

© 2017 Toby's Epril Suffusion theme by Sayontan Sinha