2012. 7. 23. 09:24 IT

[배경]  


- 문서따로 소스따로를 해결하고 싶었다. 

- 매번 변경이 생길때마다 둘다를 손보는건 번거롭다.

- 구글은 REST Java 에 대해 소스로부터 문서 자동 생성으로 방향을 잡았다고 들었다.


[전제사항]


- Spring MVC 를 쓰고 있다. (혹은 annotation에 기반한 개발을 하고 있다.. )

- 대상 클래스들이 classpath에 포함되어 있어야 한다. 



[처리흐름]


1. filter Controller class // 자바소스 중에서 Controller, RequestMapping Annotation을 가진 class들을 추출한다. 


2. filter RequestMapping Method // 추출된 class들에서 RequestMapping Annotation을 가진 method들을 추출한다.


3. get return type // 추출된 method에서 return type을 가져온다. 

    // method.getGenericReturnType()


4. get request mapping method // 사용하고 있다면 mapping method 값도 가져온다. (cf. REST)


5. filter RequestParam arguments // method 의 RequestParam 으로 정의한 parameter 정보도 추출한다. 


6. do pretty print // 예쁘게 출력한다.



[출력결과 샘플]


### XXXXX Specification Document

### Created Mon Jul 23 09:12:08 KST 2012 ###

============================

Resource : xxxxxxx


GET URI : <BASE_URL>/xxxxxxx/{xxxxxxid}

PAREMETERS : N/A

RETURN : Sample

RETURN DETAIL :

{

    "Sample": [

        "ID": "long",

        "Name": "String"

     ]

}



[회고]


1. java, Spring이 제공하는 Annotation 만으로는 문서를 만드는데 필요한 충분한 정보가 부족하더라.

ex. description, 예외처리정보 등... => 개발초기에 custom annotation 을 고려해야겠더라. 


2. 엑셀, 워드로 뽑아주려고 했더니 회사 문서보안시스템이 허용하지 않더라. 

혹시 누군가 필요로 한다면 MS가 추가한 xml의 축복을 누리시길. ( http://www.docx4java.org )


3. 재미나는 작업이었지만 결국 실제적인 도움을 주지는 못했다. (자동생성하기에는 담을 수 있는 내용이 부족했다.) => 다음엔 좀더 가치있게 되도록 사전에 고려를..


4. 반대로 Spec 문서에서 Skeleton 코드를 생성할 수도 있지 않을까... 

(가능은 하지만 당장은 One source, Multi use 를 하는게 더 정신건강에 이로울듯 싶다. ) 

posted by smplnote
2012. 4. 27. 11:00 IT

WHY?

ROLLBACK 하려고 하는데 다른 곳에서 참조한 문서들로는 잘 안먹더라...


example


@TransactionConfiguration(transactionManager="txManager", defaultRollback=true) // transactionManager id나 name을 명시..

public class YourRollbackTest {

@Transactional

@Test public void testRollbackTest() throws Exception{

                // do test 
         }

}



@Test 어노테이션이 붙은 곳에서 @Transactional을 사용하면 항상 rollback 처리해주네요.. 

posted by smplnote
2012. 4. 27. 09:23 IT

WHY?

프로젝트에서 SpringMVC의 controller를 점점 복잡하게 작성하는 경향이 발생함.(서비스 로직을 웹에서 구현하는 것으로 보임.. )

테스트 코드가 있어야 안전하게 리팩토링이 가능할테니... 

springframework이 버전을 올리면서 복잡해지는 것도 있지만 이런 장점들이 계속 쌓여 나가는 것이 마음에 든다.  



example


import static org.junit.Assert.*;

import static org.springframework.test.web.ModelAndViewAssert.*;

import org.springframework.mock.web.*;

import org.springframework.web.servlet.*;

import org.springframework.web.servlet.mvc.annotation.*;

import org.springframework.context.*;

import org.codehaus.jettison.json.JSONObject;

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(locations = {"file:src/.../xxx.xml" })

public class TistoryTest {

@Inject  ApplicationContext applicationContext;

@Inject AnnotationMethodHandlerAdapter handlerAdapter;

@Inject DefaultAnnotationHandlerMapping handlerMapping;

MockHttpServletRequest request;
MockHttpServletResponse response;
/// MockHttpSession session; // if you need session data...
ModelAndView mav;

        // 테스트할 spring MVC controller 
         YourTestController controller;

@Before
public void setUp() throws Exception {
response = new MockHttpServletResponse();
request = new MockHttpServletRequest();
/// session = new MockHttpSession(null);
}
@After
public void tearDown() throws Exception {
/// session.invalidate();
}

        // ViewName을 반환하는 일반적인 케이스를 위한 테스트 
@Test  public void testViewNameReturnMethod () throws Exception{
// [given]
                request.setRequestURI("/tistory.do");
                request.setMethod("GET");
request.addParameter("method"," viewNameReturnMethod "); // if you using method style
request.addParameter("yourParam","yourValue"); // set parameter
                /// saveSession("MYKEY","MYVALUE");

// [when]
handleInterceptor(controller);
                handlerAdapter.handle(request, response, controller);

// [then]
assertViewName(mav, "yourViewName"); // check viewname
String attr1 = assertAndReturnModelAttributeOfType(mav,
"yourAttribute1", String.class);
assertEquals(attr1,"expected result");
}

        // ajax 통신을 할 경우 직접 ResponseBody에 출력하기 때문에 response의 내용으로 테스트를 수행한다. 
@Test public void testJsonReturnMethod() throws Exception{
// [given]
                request.setRequestURI("/tistory.do");
                request.setMethod("GET");
request.addParameter("method","jsonReturnMethod"); // if you using method style
request.addParameter("yourParam","yourValue"); // set parameter
                /// saveSession("MYKEY","MYVALUE");

// [when]
handleInterceptor(controller);
                handlerAdapter.handle(request, response, controller);

// [then]
               JSONObject json = new JSONObject(response.getContentAsString());
               String result = json.getString("your_result_json_key");
assertEquals(result,"expected json value");
}
protected void handleInterceptor(Object controller) throws Exception {
for (final HandlerInterceptor interceptor : handlerMapping.getHandler(
request).getInterceptors()) {
interceptor.preHandle(request, response, controller);
}
}
/*  protected void saveSession(String key, Object value) {
session.putValue(key, value);
request.setSession(session);
}  */
}

[주의사항]
session scoped bean을 사용할 경우 다음 에러가 발생함 : 
java.lang.IllegalStateException: No Scope registered for scope 'session'
아래 참고자료를 이용하여 해결할 것.
ref : http://blog.solidcraft.eu/2011/04/how-to-test-spring-session-scoped-beans.html

posted by smplnote