<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                # 追求代碼質量: 對 Ajax 應用程序進行單元測試 _使用 GWT 更輕松地測試異步應用程序_ 您可能從編寫 Ajax 應用程序中獲得了極大樂趣,但是對它們執行單元測試卻著實讓人頭痛。 在本文中,Andrew Glover 著手解決 Ajax 的弱點(其中之一),即應對異步 Web 應用程序執行單元測試的固有挑戰。 幸運的是,他發現在 Google Web Toolkit 的幫助下,解決這個特殊的代碼質量問題要比預想的容易。 _Ajax_ 在近期無疑是 Web 開發界最時髦的字眼之一 —— 與 Ajax 相關的工具、框架、書籍以及 Web 站點的劇增就是該技術流行的最好證明。 此外,Ajax 應用程序也相當靈巧,不是嗎?不過,像任何一個開發過 Ajax 應用程序的人證實的一樣, 對 Ajax 執行測試真的很不方便。事實上,Ajax 的出現已經從根本上使得許多測試框架和工具_失效_,因為它們并沒有針對異步 Web 應用程序測試進行設計! 有趣的是,某個支持 Ajax 的框架的開發人員注意到了這個限制, 并為此做了一些非常新穎的設計:內置的可測試性。除此之外,由于該框架簡化了使用 Java? 代碼(而不是 JavaScript)創建 Ajax 應用程序,它的起點甚高,并且充分利用了 Java 平臺上無可置疑的標準測試框架:JUnit。 我所論及的框架當然是非常流行的 Google Web Toolkit,也就是 GWT。在本文中,我將向您展示 GWT 如何實際地利用 Java 兼容性, 使 Ajax 應用程序的每個部分都能像與之對應的同步應用程序一樣進行測試。 ## 改進代碼質量 別錯過 Andrew Glover 的 [代碼質量討論論壇](http://www.ibm.com/developerworks/forums/dw_forum.jsp?S_CMP=cn-a-j&S_TACT=105AGX52&forum=812 cat=10),里面有關于代碼語法、測試框架以及如何編寫專注于質量的代碼的幫助。 ## JUnit 和 GWTTestCase 因為與 GWT 有關的 Ajax 應用程序采用 Java 代碼編寫, 所以非常適合開發人員使用 JUnit 進行測試。 事實上,GWT 開發小組還為此創建了一個幫助器類 `GWTTestCase`,擴展自 JUnit 的 3.8.1 `TestCase`。 該基類添加了一些功能,可測試 GWT 代碼并處理某些基礎實現從而啟動并運行 GWT 組件。 ## Google Web Toolkit Google Web Toolkit 在 Java Web 開發社區的發布聲勢浩大, 同時也獲得了與之相稱的巨大轟動。GWT 為利用 Java 代碼進行設計、構建和部署支持 Ajax 的 Web 應用程序提供了一種新穎的方式。Java Web 開發人員不再需要學習 JavaScript 并花費數個小時解決特定于瀏覽器的問題,他們可以直接進行與 Ajax 有關的富含信息的動態 Web 應用程序設計。 需要提醒的是:`GWTTestCase` 并非用來測試 與 UI 相關的代碼 —— 它是為了便于測試那些由 UI 交互_觸發_ 的異步問題。對 `GWTTestCase` 用途的誤解使許多剛接觸 GWT 的開發人員備受挫折, 因為他們期望能夠用它方便地模擬用戶界面,但最終發現這是徒勞的。 Ajax 組件有兩個基本組成: 體驗和功能,這些都被設計成異步方式。 圖 1 演示了一個模擬 Web 表單的簡單 Ajax 組件。 由于該組件支持 Ajax,表單的提交是異步執行的(即:無需重新載入與傳統表單提交關聯的頁面)。 ##### 圖 1\. 一個支持 Ajax 的簡單 Web 表單 ![](https://box.kancloud.cn/2016-05-11_5732e0bce8548.jpg) 輸入一個有效單詞,單擊組件的 **Submit** 按鈕,將向服務器發送消息請求該單詞的定義。 該定義通過回調異步返回,相應地插入到 Web 頁面,如圖 2 所示: ##### 圖 2\. 單擊 Submit 按鈕后顯示響應 ![](https://box.kancloud.cn/2016-05-11_5732e0c79e804.jpg) ### 功能性和集成測試 圖 2 所示的交互測試可用于多個不同場景, 但是其中兩種場景最為常見。從功能性觀點考慮,您或許希望編寫一個測試:填入表單值,單擊 **Submit** 按鈕,然后驗證表單是否顯示定義。 另外一個選擇是集成測試,使您能夠驗證客戶端代碼的異步功能。GWT 的 `GWTTestCase` 正是 被設計用來執行此類測試。 需要牢記的是:在 `GWTTestCase` 測試用例環境下不可以進行用戶界面測試。 在設計和構建 GWT 應用程序時,您必須清楚_不要依賴用戶界面_ 測試代碼。 這種思路需要把交互代碼從業務邏輯中分離出來, 正如您已經了解的,這是最佳的入門實踐! 舉例而言,重新查看圖 1 和圖 2 所示的 Ajax 應用程序。 該應用程序由四個邏輯部分構成:`TextBox` 用于輸入目標單詞,`Button` 用于執行單擊,還有兩個 `Label`(一個用于 `TextBox`,另一個顯示定義)。 實際 GWT 模塊的初始方法如清單 1 所示, 但是您該如何測試這段代碼呢? ##### 清單 1\. 一個有效的 GWT 應用程序,但是如何測試它? ``` public class DefaultModule implements EntryPoint { public void onModuleLoad() { Button button = new Button("Submit"); TextBox box = new TextBox(); Label output = new Label(); Label label = new Label("Word: "); HorizontalPanel inputPanel = new HorizontalPanel(); inputPanel.setStyleName("input-panel"); inputPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE); inputPanel.add(label); inputPanel.add(box); button.addClickListener(new ClickListener() { public void onclick(Widget sender) { String word = box.getText(); WordServiceAsync instance = WordService.Util.getInstance(); try { instance.getDefinition(word, new AsyncCallback() { public void onFailure(Throwable error) { Window.alert("Error occurred:" + error.toString()); } public void onSuccess(Object retValue) { output.setText(retValue.toString()); } }); }catch(Exception e) { e.printStackTrace(); } } }); inputPanel.add(button); inputPanel.setCellVerticalAlignment(button, HasVerticalAlignment.ALIGN_BOTTOM); RootPanel.get("slot1").add(inputPanel); RootPanel.get("slot2").add(output); } } ``` 清單 1 的代碼在運行時發生了嚴重的錯誤:它無法_按照_ JUnit 和 GWT 的 `GWTTestCase` 進行測試。 事實上,如果我試著為這段代碼編寫測試,從技術方面來說它可以運行,但是無法按照邏輯工作。考慮一下:您如何對這段代碼進行驗證?惟一可用于測試的 `public` 方法返回的是 `void`, 那么,您怎么能夠驗證其功能的正確性呢? 如果我想以白盒方式驗證這段代碼,就必須分離業務邏輯和特定于用戶界面的代碼,這就需要進行重構。這本質上意味著把清單 1 中的代碼分離到一個便于測試的獨立方法中。 但是這并非聽上去那么簡單。很明顯組件掛鉤是通過 `onModuleLoad()` 方法實現, 但是如果我想強制其行為,_可能_ 必須操縱某些用戶界面(UI)組件。 * * * ## 分解業務邏輯和 UI 代碼 第一步是為每個 UI 組件創建訪問器方法, 如清單 2 所示。按照該方式,我可以在需要時獲取它們。 ##### 清單 2\. 向 UI 組件添加訪問器方法使其可用 ``` public class WordModule implements EntryPoint { private Label label; private Button button; private TextBox textBox; private Label outputLabel; protected Button getButton() { if (this.button == null) { this.button = new Button("Submit"); } return this.button; } protected Label getLabel() { if (this.label == null) { this.label = new Label("Word: "); } return this.label; } protected Label getOutputLabel() { if (this.outputLabel == null) { this.outputLabel = new Label(); } return this.outputLabel; } protected TextBox getTextBox() { if (this.textBox == null) { this.textBox = new TextBox(); this.textBox.setVisibleLength(20); } return this.textBox; } } ``` 現在我實現了對所有與 UI 相關的組件的編程式訪問(假設所有需要進行訪問的類都在同一個包內)。以后我可能需要使用其中一種訪問進行驗證。我現在希望_限制_ 使用訪問器,如我已經指出的,這是因為 GWT 并非設計用來進行交互測試。所以,我不是真的要試圖測試某個按鈕實例是否被單擊, 而是要測試 GWT 模塊是否會對給定的單詞調用服務器端代碼,并且服務器端會返回一個有效定義。方法為將 `onModuleLoad()` 方法的定義獲取邏輯推入(不是故意用雙關語!)一個可測試方法中,如清單 3 所示: ##### 清單 3\. 重構的 onModuleLoad 方法委托給更易于測試的方法 ``` public void onModuleLoad() { HorizontalPanel inputPanel = new HorizontalPanel(); inputPanel.setStyleName("disco-input-panel"); inputPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE); Label lbl = this.getLabel(); inputPanel.add(lbl); TextBox txBox = this.getTextBox(); inputPanel.add(txBox); Button btn = this.getButton(); btn.addClickListener(new ClickListener() { public void onClick(Widget sender) { submitWord(); } }); inputPanel.add(btn); inputPanel.setCellVerticalAlignment(btn, HasVerticalAlignment.ALIGN_BOTTOM); if(RootPanel.get("input-container") != null) { RootPanel.get("input-container").add(inputPanel); } Label output = this.getOutputLabel(); if(RootPanel.get("output-container") != null) { RootPanel.get("output-container").add(output); } } ``` 如清單 3 所示,我已經把 `onModuleLoad()` 的定義獲取邏輯委托給 `submitWord` 方法, 如清單 4 定義: ##### 清單 4\. 我的 Ajax 應用程序的實質! ``` protected void submitWord() { String word = this.getTextBox().getText().trim(); this.getDefinition(word); } protected void getDefinition(String word) { WordServiceAsync instance = WordService.Util.getInstance(); try { instance.getDefinition(word, new AsyncCallback() { public void onFailure(Throwable error) { Window.alert("Error occurred:" + error.toString()); } public void onSuccess(Object retValue) { getOutputLabel().setText(retValue.toString()); } }); }catch(Exception e) { e.printStackTrace(); } } ``` `submitWord()` 方法又委托給 `getDefinition()` 方法,我可以用 JUnit 測試它。`getDefinition()` 方法從邏輯上獨立于特定于 UI 的代碼(對于絕大部分而言),并且可以在沒有單擊按鈕的情況下得到調用。另一方面, 與異步應用程序有關的狀態問題和 Java 語言的語義規則也規定了我不能在測試中_完全_ 避免與 UI 相關的交互。仔細查看清單 4 中的代碼,您能夠發現激活異步回調的 `getDefinition()` 方法操縱了某些 UI 組件 —— 一個錯誤警告窗口以及一個 `Label` 實例。 我還可以通過獲得輸出 `Label` 實例的句柄,斷言其文本是否是給定單詞的定義,從而驗證應用程序的功能。 在用 `GWTTestCase` 測試時,最好_不要_ 嘗試手工 強制改變組件狀態,而應該讓 GWT 完成這些工作。舉例而言,在清單 4 中,我想驗證對某個給定單詞返回了其 正確定義并放入一個輸出 `Label` 中。無需操作 UI 組件來設置這個單詞;我只要直接調用 `getDefinition` 方法,然后斷言 `Label` 具有對應定義。 既然我已經編寫好了計劃進行測試的 GWT 應用程序,我需要實際編寫測試,這意味著設置 GWT 的 `GWTTestCase`。 * * * ## 設置 GWTTestCase 若想從 `GWTTestCase` 的測試魔力中獲益,需要遵守一些規則。 幸運的是,規則很簡單: * 所有用于實現測試的類和待測 GWT 模塊必須位于同一個包內。 * 運行測試時,您必須至少傳遞一個 VM 參數,指明在哪種 GWT 模式(托管或 Web)下運行測試。 * 您必須實現 `getModuleName()` 方法,它返回一個 `String`,表示您的 XML 模塊文件。 最后,因為與服務器端實體通信的 Ajax 應用程序在本質上是異步的,GWT 還提供了 `Timer` 類 , 以便延遲 JUnit,使異步行為在進行相關斷言之前全部完成。 ### 實現 getModuleName 和 Timer 類 我已經指出,我的測試集中于 `getDefinition()` 方法(如 [清單 4](#listing4) 所示)。 您可以從代碼看到,測試邏輯非常簡單:傳入一個單詞(比如 _pugnacious_),然后驗證相應的 `Label` 文本是否得到正確定義。很簡單,對嗎?但是不要忘記,`getDefinition()` 方法在 `AsyncCallback` 對象中具有某種相關的異步性。 `GWTTestCase` 類是一個_抽象_ 類,因為它的 `getModuleName()` 方法就是這么聲明的;因此,當您 擴展該類時,您需要實現 `getModuleName()`(除非您是在為框架創建自己的基抽象類)。模塊名實際上就是您的 GWT XML 文件所在的包結構的名稱去掉文件擴展名。舉個例子,在本例中, 我有一個名為 WordModule.gwt.xml 的 XML 文件,它位于 一個目錄結構如:com/acme/gwt。相應的, 模塊的邏輯名稱為 `com.acme.gwt.WordModule`, 這會讓您想到 Java 平臺的普通包模式。 我已經得到一個模塊名,可以開始定義測試用例了,如清單 5 所示: ##### 清單 5\. 您必須實現 getModuleName 方法并提供一個有效的名字 ``` import com.google.gwt.junit.client.GWTTestCase; import com.google.gwt.user.client.Timer; public class WordModuleTest extends GWTTestCase { public String getModuleName() { return "com.acme.gwt.WordModule"; } } ``` 到目前為止一切良好,但是我還沒有執行任何測試!由于我的 Ajax 應用程序使用 `AsyncCallback` 對象,在通過測試用例調用 `getDefinition()` 方法時, 我必須強迫 JUnit 延遲運行;否則測試將由于沒有任何響應而失敗。這就要用到 GWT 的 `Timer` 類。`Timer` 使我能夠重寫 `getDefinition()` 的 `run` 方法,在 `Timer` 內完成測試用例邏輯。(測試用例以獨立線程運行,有效地阻塞 JUnit 完成整個測試用例)。 以我的測試為例,我將首先調用 `getDefinition()` 方法,然后提供一個 `Timer` 的 `run()` 方法的實現。`run()` 方法得到輸出 `Label` 實例的文本并驗證是否是正確定義。 定義了 `Timer` 實例后,我就需要確定其何時運行,同時強制 JUnit 掛起直至 `Timer` 實例完成。也許聽起來有點復雜,不必擔心,因為實踐起來非常簡易。實際上,清單 6 展示了整個過程: ##### 清單 6\. 使用 GWT 輕松測試 ``` public void testDefinitionValue() throws Exception { WordModule module = new WordModule(); module.getDefinition("pugnacious"); Timer timer = new Timer() { public void run() { String value = module.getOutputLabel().getText(); String control = "inclined to quarrel or fight readily;..."; assertEquals("should be " + control, control, value); finishTest(); } }; timer.schedule(200); delayTestFinish(500); } ``` 正如您所見,`Timer` 的 `run()` 方法 是我真正驗證 Ajax 應用程序功能及其應用遠程過程調用的地方。 請注意 run 方法的最后一步是調用 `finishTest()` 方法, 它意味著一切如預期運行,JUnit 可以不受阻塞正常運行。 在實踐中,您可能會發現需要根據異步行為完成所需的時間調整延遲時間。 但用 JUnit 測試 GWT 應用程序的要點在于:您能夠在_無需_ 部署完整功能的 Web 應用程序的情況下測試它。因此,您能夠_更早地_ 并且更 _頻繁地_ 測試您的 GWT 應用程序。 * * * ## 運行 GWT 測試 ## 使用 GWT 進行功能測試 像本文演示的這類簡單 Ajax 應用程序_可以_ 從功能角度進行驗證, 使用包括 Selenium 在內的框架,它會驅動瀏覽器模擬實際用戶行為。不過,要想用 Selenium 運行功能測試,您必須部署完整功能的 Web 應用程序。 前面我曾提到,如果您想實際運行您的 GWT JUnit 測試,您必須執行大量瑣碎的工作來配置運行環境。比如說,要想通過 Ant 的 `junit` 任務運行我的測試,我就必須確保某些文件 位于類路徑中并向低層 JVM 提供一個參數。特別是,在調用 `junit` 任務時, 我還要確保托管源文件(以及測試)的目錄(或多個目錄)位于類路徑中,還要告訴 GWT 以何種模式運行。 我傾向于使用 _hosted_ 模式,這意味著要使用 _www-test_ 標志 ,如清單 7 所示: ##### 清單 7\. 用 Ant 運行 GWT 測試 ``` <junit dir="./" failureproperty="test.failure" printSummary="yes" fork="true" haltonerror="true"> <jvmarg value="-Dgwt.args=-out www-test" /> <sysproperty key="basedir" value="." /> <formatter type="xml" /> <formatter usefile="false" type="plain" /> <classpath> <path refid="gwt-classpath" /> <pathelement path="build/classes" /> <pathelement path="src" /> <pathelement path="test" /> </classpath> <batchtest todir="${testreportdir}"> <fileset dir="test"> <include name="**/**Test.java" /> </fileset> </batchtest> </junit> ``` 運行 GWT 測試現在轉變成調用問題了。還需注意的是 GWT 測試屬于輕量級測試, 所以我可以頻繁運行測試,甚至是連續運行,就像我在一個持續集成環境(Continuous Integration)中一樣。 * * * ## 結束語 在本文所示的 GWT 測試用例中,您已經看到用于驗證 Ajax 應用程序所需的基本步驟。 您可以繼續測試我的示例 GWT 應用程序,比如測試一些邊界用例,但是我認為重點在于:如果使用包含測試特性的框架編寫 Ajax 應用程序,測試要比想象中容易。 要對 GWT 應用程序進行良好測試(對絕大多數應用程序也適用),關鍵在于設計應用程序時要把測試一并考慮。還要注意 `GWTTestCase` 不是被用來進行交互測試的。 您不能使用 `GWTTestCase` 直接模擬用戶。不過您能夠以一種間接的方式用它來驗證用戶交互,正如本文中演示的那樣。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看