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.... 還有... 這些都待下次在說吧,再研究,再研究。