領先一步
VMware 提供培訓和認證,助你快速提升。
瞭解更多在過去幾個月裡,我一直與各種客戶合作開展使用 Google Web Toolkit [GWT] 的專案。我喜歡 GWT,主要是因為它提供了 Java 到 javascript 的編譯器。這是讓普通 Java 開發人員無需學習新語言即可建立 RIA 的關鍵。
我一直以來都是測試驅動開發(TDD)的擁躉,令我失望的是,乍一看,GWT 和 TDD 似乎無法協同工作。
測試 GWT 程式碼有些問題。核心問題在於 GWT 程式碼在執行前被編譯成 javascript。在許多情況下,GWT.create() 語句用於連線動態繫結機制。在普通的 Java 環境中執行時,這個語句會導致異常。
即使你使用像 EasyMock 這樣的模擬庫來模擬出導致問題的部分,如果它是在建構函式中觸發的,你仍然可能遇到問題。為所有 widget 建立介面開銷太大了,而且即使你這樣做了,你也無法測試包含 GWT.create() 的特定類。
GWT 在 GWTTestCase 中提供了這個問題的解決方案,但這個類本身也存在不少問題。僅舉幾例:
當你進行測試驅動開發時,你需要能夠編寫至少具有以下特性的測試:
那麼,我們該怎麼做才能避免因為缺乏測試導致程式碼變成“麵條”,同時儘量減少那些緩慢的 GWTTestCases 呢? MVC 來拯救!
MVC 是一個久經考驗的模式。它有很多應用,其中一些除了名字之外與原始 MVC 模式幾乎沒有什麼共同點,但我想指出的總體方向最好由 Martin Fowler 在 Humble View(謙遜檢視)這個名字下總結。
我更簡短的總結是這樣的:檢視通常很難測試,因此它們應該知道得越少越好,做得也越少越好。在 GWT 中,檢視等同於 Widget。這裡有一個小問題:任何在客戶端執行的東西都由 Widget 擁有。大多數 GWT 程式碼幾乎把所有的邏輯都放在了 widget 中,所以大多數使用 GWT 的開發者都透過繼承一個 widget 然後新增一些邏輯來完成工作。
我的建議很簡單:不要那樣做。型別安全對於獲得 IDE 支援來說很好,但它並不能阻止你寫出糟糕的程式碼。所以如果你不做單元測試,你的程式碼最終會變得一團糟。
如果你不把邏輯放在檢視(Widget 的子類)中,你需要另一個地方來放它。這就是 Controller 發揮作用的地方。回到我之前提到的小問題,我們將需要從 Widget 或 EntryPoint 中引導 Controller。這確實是無法避免的,所以我們就這樣做,看看它看起來有多糟。
public class NoteEditor extends Composite {
public NoteEditor() {
//do the dependency injection stuff
NoteModel noteModel = new NoteModel();
NoteEditorController controller =
new NoteEditorController(noteModel, NoteService.App.getInstance(), new AlertCallback());
//...
}
}
如你所見,我在這裡進行了一些程式碼中的依賴注入,因為客戶端還沒有 Spring。我說“還沒有”,是因為之後我們可以為此使用 GWToolbox 或 Rocket。服務透過 GWT.create() 來引導,而 AlertCallback 用於將 Controller 與 Window 解耦,以便處理偶爾的彈窗。我覺得這還不算太糟,不為此程式碼寫測試我也能睡個好覺。問題還沒有完全解決,因為我們想在檢視中使用的任何元素(按鈕、標籤等)都需要在檢視中例項化,然後註冊到 Controller 中。
controller.registerDetailViewSelector(new DeckPanelSelector(detailView));
detailView.addStyleName("detailPanel");
main.add(detailView, DockPanel.CENTER);
//some buttons at the bottom of the screen
buttonPanel.add((Widget)controller.registerClearButton(new Button("Clear")));
buttonPanel.add((Widget)controller.registerLoadButton(new Button("Load existing Note")));
Controller 透過它們的介面 (SourcesClickEvents) 接受按鈕,這帶來的好處是我們可以用外觀不同的 Widget 替換按鈕,而無需修改 Controller。這沒什麼新鮮的,這正是 MVC 所倡導的關注點分離。老實說,我通常更喜歡寫一個測試來檢查註冊是否正確發生,但這是沒有 GWTTesCase 就無法做到的。現在是時候讓我們的 IDE 為我們建立那個 Controller + 方法,並編寫測試,這樣我們就可以實現邏輯了。例如,載入按鈕的測試看起來是這樣的:
public void testLoadButtonPressed_success() throws Exception {
final Foo expectedFoo = new Foo("expected");
fooServiceMock.loadFoo(isA(String.class), isA(AsyncCallback.class));
expectLastCall().andAnswer(new IAnswer() {
public Object answer() throws Throwable {
((AsyncCallback) getCurrentArguments()[1]).onSuccess(expectedFoo);
return null;
}
});
fooModelMock.setFoo(expectedFoo);
replay(allMocks);
loadButton.fireClick();
verify(allMocks);
}
然後就可以開始了!我附上了一個快速示例來展示這個想法。你可以將其用作實驗的起點。
更新於 2008 年 10 月:示例原始碼已過時。總體建議仍然有效。
總結一下 GWT 風格的 MVC 模式的快速概覽。但當然你可以選擇自己的風格,只要你將邏輯儲存在可測試的類中。