Wicket, Wicket, Wicket ! 喔!整個禮拜時間都耗在它上面,感想是... Wicket 很新,非常非常的新。我覺得與其稱他為 1.1 版,其實他大概只有 0.7, 0.8 版這個程度而已。到不是程式的品質不夠,而是一些決策性的功能還沒有完全的定案。除了核心開發人員有用在production外,其他人多半還是在研究評估的階段,也有不少是拿他當作下班後的興趣 :-) 它的文件很少,目前多半要去看他的 source 和 wiki。Javadoc 寫的很詳細,也是不錯的參考。而 wiki 上則有很多範例和經驗分享。如果真的現在要拿來開發研究,Mailing List是一定要訂閱的,而且上面幾乎是有問必答 (只是寫英文有點累啦...),整個社群算滿活躍的 (最近登上 SourceForge 一百大)
anyway, 如果要能夠取代目前我們專案中所用的 web 技術組合:SiteMesh / StrutsTestCase / Spring / ACEGI security / DisplayTag / Struts / Validation Framework... 等等。以現在 Wicket 來說還是蠻吃力的。當然啦,拿一個小 Framework 跟上面一大堆的技術來比當然是不公平的... 但還是要比較啊,誰叫 Wicket 跟上面的都不相容呢?別的 jsp-based 的 framework 只要做到一些簡單的功能就夠了,因為它們可以跟上面提到的工具整合,合起來也是嚇嚇叫。Wicket 就沒這福份了,全部得自己提供... 這也是 Wicket 主要的弱點之一。
首先,最重要的是先看看 Test 該怎麼寫。我們寫 Struts 有好用的 StrutsTestCase,Wicket 也有提供類似的工具,我們後面再提。Wicket 開發人員是比較建議用 integration test -- jWebUnit 。這種萬用型的 http 測試工具,當然 Wicket 也能搭配使用。不過我們可以搭配 Jetty Server,然後只設定 WicketServlet,這樣測試會比較快:
public abstract class JettyJWebUnitTestCase extends WebTestCase {
private static Server server;
private ServletContext servletContext;
private static final String LOCALHOST = "localhost";
private static final int CONFIDENTIALPORT = 8443;
private static final int LOW_RESOURCE_PERSIST_TIME = 2000;
private static final int MAX_IDLE_TIME = 30000;
private static final int MAX_THREADS = 10;
//啟動 jetty server 並設定 WicketServlet
protected void startServerForMockWicketServlet() throws Exception {
server = new Server();
SocketListener listener = new SocketListener();
listener.setHost(LOCALHOST);
listener.setPort(CONFIDENTIALPORT);
listener.setMaxThreads(MAX_THREADS);
listener.setMaxIdleTimeMs(MAX_IDLE_TIME);
listener.setLowResourcePersistTimeMs(LOW_RESOURCE_PERSIST_TIME);
listener.setConfidentialPort(CONFIDENTIALPORT);
server.addListener(listener);
buildMockWicketServlet();
server.start();
}
//關閉舊的 WicketServlet,再重新啟動一次。
protected void restartMockWicketServlet() throws Exception {
ServletHttpContext context = (ServletHttpContext) server
.getContext("/wicket");
context.stop();
server.removeContext(context);
buildMockWicketServlet().start();
}
private ServletHttpContext buildMockWicketServlet()
throws Exception {
//設定 webapp 為 "/wicket"
ServletHttpContext context = (ServletHttpContext) server
.getContext("/wicket");
//替這個 webapp 為 加上一個Servlet: WicketServlet
ServletHolder holder = context.addServlet("BookApplication",
"/book","wicket.protocol.http.WicketServlet");
//下面是 WicketServlet 的啟始參數
holder.setInitParameter("applicationClassName",
"javaworld.wicket.BookApplication");
servletContext = holder.getServletContext();
return context;
}
protected final void stopServer() throws Exception {
if (server != null) {
server.stop();
server = null;
}
}
}
上面的 startServerForMockWicketServlet() 建立 Jetty Server,然後 buildMockWicketServlet() 手動設定一個 WicketServlet到 Server 裡,而後再啟動 server。如此,這個 Jetty Server 就只有一個 Servlet 而已,size 小很多。每一次執行 testMethod 時,就用 restartMockWicketServlet() 重開 servlet。開關一次大概約需兩秒吧。雖然還是很慢,但是已經比啟動整個 server 好多了。而且這個 Server 也沒有 Filter/Spring config 等等的設定,所以並沒有其他的 overhead,專門用來測試 Wicket 的WebPage (如果你的 Wicket Page 裡面要用到 Spring 的 Bean,那就要花點技巧 inject 到這個沒有 Spring 的 webapp,有機會下次再說吧)
上面提到,Wicket 也有提供 without container 的測試工具。在他的 test 目錄底下,有MockWebApplication 可以使用。使用方法如下:
//正式的程式碼:
public class BookHomePage extends WebPage {
public BookHomePage() {
add(new Label("message", "welcome"));
}
}
//Unit Test Method
public void testBookHome() throws Exception {
//建立 Mock Wicket 工作環境
WebApplication mockWebApp = new MockWebApplication();
//設定欲測試的 WebPage 為首頁
mockWebApp.getPages().setHomePage(BookHomePage.class);
//初始化 reqeust/response
mockWebApp.setupRequestAndResponse();
//開始 request
mockWebApp.processRequestCycle();
//取得 page render 後的結果
WebPage resultPage = mockWebApp.getLastRenderedPage();
//開始 assert component 內容
Label label = (Label) resultPage.get("message") ;
assertEquals("welcome", label.getModelObjectAsString());
}
手續蠻複雜的,不過只要做一些功,把常用的寫成 util 就可以簡化了。這是最簡單的測試 ,如果要測試複雜的 component 例如 PagableList 會比較麻煩一點。而 Form 的測試目前還沒有什麼範例可看... orz
Test 還要花點時間研究... 暫時先這樣啦... 而 SiteMesh 的功能,Wicket 也有類似的solution,還有 Spring 的 integration.... 還有... 這些都待下次在說吧,再研究,再研究。