国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

成為GUI的最好的朋友并揭開Liar View的面紗

2019-11-18 15:05:11
字體:
來源:轉載
供稿:網友

  內容:
Liar, liar!
Liar View 錯誤模式
癥狀
起因
治療及預防措施
總結
參考資料

GUI 一般是基于模型-視圖-控制器體系結構設計的,其中,視圖是從模型中分離出來的。這種分離對自動測試是一個挑戰,因為我們很難檢驗模型中的狀態改變是否在視圖中得到了適當的反映,這樣就產生了臭名昭著的 Liar View。在診斷 java 代碼的這部分中,討論的就是 Liar View 錯誤模式。
Liar, liar!
設想一下:您已經為一個分布式系統精心設計了一個極好的 GUI 程序,它包含了客戶機請求的所有東西及其它一些東西。您已經讓它運行通過了一個自動化測試套件的測試 ? 由于不變量的數量是個天文數字,因此,自動化測試是必須的。測試的結果是程序獲得了一張“無錯誤的健康證實書”。

發布這個 GUI 的期限到了,但是,作為一個象您這樣嚴格的程序員,只是為了發現錯誤的行為 ? 本該在自動化測試中就被捕捉到的行為,您啟動了程序,對它做最后一次手工測試。但愿您能夠避免這種情形。真的,您能夠。

Liar View 錯誤模式
好的調試從好的測試開始。由于 GUI 程序中有大量的不變量需要檢查,因此,自動化測試是必需的。但有時盡管已經通過了一套測試,在手工檢查時,程序仍會出現本該由這些測試之一發現的錯誤行為。

快速跟蹤代碼
清單 1. JTable 及表模型 一個說明 Liar View 如何產生的簡單 GUI
清單 2. 檢查視圖、模型及行的內容使斷言說明真相

在分布式和多線程系統中,這種行為是常見的。在這些情況下,程序的“不確定性”本質經常就是原因所在。但在 GUI 中,卻有另一種常見的原因 ? Liar View 錯誤模式。

癥狀
多數 GUI 程序測試,跟通常的程序測試一樣,遵循下列步驟:

啟動程序
檢查程序狀態的一些特征
嘗試修改狀態
檢查狀態是否已經按意愿被修改

但就象我已提到過的,有時對運行時程序行為的手工檢查的結果會與測試得到的成功結果相矛盾:屏幕上可能顯示一個隊列,包含被測試(按設想進行)確認刪除了的元素;而對象中可能包含報告顯示已被更新了的陳舊數據。

類似這樣的錯誤會使我們對自己的心智是否健全產生懷疑,或者更糟地陷入到康德的懷疑論哲學,懷疑起原因本身的有效性。

不要讓這些發生在您身上。當正確對待原因時,它確實是有用的。盡管有反面的報告,但很少有程序員會在寫代碼時永久地喪失心智( 永久地是一個要害詞)。

起因
找到這些錯誤的一個要害是要熟悉到,至少有一部分錯誤可以在測試套件中找到。

在測試 GUI 程序的過程中,錯誤最常發生的地方是在最后一步:檢查狀態是否已經按意愿被修改。原因是 GUI 一般是基于模型-視圖-控制器(MVC)體系結構設計的。Swing 類庫甚至把這種體系結構建到了 GUI 類自身的結構中。

在 MVC 體系結構中,程序的內部狀態保存在模型中。視圖響應改變模型狀態的事件并相應更新屏幕圖像。控制器把這兩個組件連接在一起。

這種體系結構的優點是把視圖從模型中分離出來,使得各自的實現可以獨立修改。但是它對自動化測試方法卻是一個挑戰:我們很難檢驗模型中的狀態改變是否在視圖中得到了適當的反映。當這兩者之間存在矛盾時,我們就會碰到一個 Liar View 錯誤模式的實例。

例如,考慮下面的簡單 GUI。它在一列元素的內容被更新時顯示其內容。Controller 類的 main 方法被用作一個簡單的測試。在實際的應用程序中,我把這個方法移到單獨的測試類中,并將其掛到 JUnit 中(請參閱參考資料)。

為了讓我們能夠使測試以慢動作方式進行,并在每個事件發生時對它進行手工檢查,我添加了 pause() 方法和 PAUSE 字段。

清單 1. JTable 及表模型

import java.awt.*;
import java.awt.event.*;
import java.util.Vector;
import javax.swing.*;

public class Controller {

PRivate static final int PAUSE = 1;

private static void assert(boolean assertion) {
if (! assertion) {
throw new RuntimeException("Assertion Failed");
}
}

private void pause() {
try {
synchronized (this) {
wait(PAUSE);
}
}
catch (InterruptedException e) {
}
}

public static void main(String[] args) {

Controller controller = new Controller();
JFrame frame = new JFrame("Test");
Model model = new Model();
JList view = new JList(model);

view.setPreferredSize(new Dimension(200,100));
frame.getContentPane().add(view);
frame.pack();
frame.setVisible(true);
assert(model.getSize() == 0);

controller.pause();
model.add("test0");

controller.pause();
model.add("test1");

controller.pause();
assert(model.getSize() == 2);

controller.pause();
model.remove(0);

controller.pause();
assert(model.getSize() == 1);

controller.pause();
System.exit(0);
}
}

class Model extends AbstractListModel {

private Vector elements = new Vector();

public synchronized Object getElementAt(int index) {
return elements.get(index);
}

public synchronized int getSize() {
return elements.size();
}

public synchronized void add(Object o) {
int index = this.getSize();
this.elements.add(o);
this.fireIntervalAdded(this, index, index);
}

public synchronized void remove(int index) {
this.elements.remove(index);
}
}

您可能已經注重到這段代碼有一個嚴重錯誤。假如我們運行這段測試代碼,所有的斷言都會是成功的,它表明已在列表中正確地添加或除去了項目。但假如我們通過某種方法,例如把 PAUSE 設成 1000,使運行速度降下來,那么我們就能以手工檢查的方式運行測試。猜猜結果是什么?我們注重到,視圖中不曾有項目被除去。

視圖沒被更新的原因是,Model 類中的 remove() 方法從未調用 fireIntervalRemoved() 來通知偵聽器:模型的狀態已經改變了。

但我們的測試方法中的所有斷言都取得了成功。為什么呢?因為這些斷言只是在模型中,而不是在視圖中檢查發生的更改。因為模型被適當地更新了,斷言沒能檢測到遺漏的事件觸發。

治療及預防措施
防止這種錯誤模式的一種方法是:只在修改模型的狀態之后才檢查視圖的顯式屬性。雖然這種技術把我們能檢查的屬性限制在視圖提供的范圍內,但至少能使斷言反映屏幕上實際正在發生的事情。

例如,我們可重寫 Controller.main,如下所示:

清單 2. 檢查視圖、模型及行的內容

import java.awt.*;
import java.awt.event*;
import java.util.Vector;
import java.swing.*;

public class Controller {

. . .

public static void main(String[] args) {

Controller controller = new Controller();
JFrame frame = new JFrame("Test");
Model model = new Model();
JList view = new JList(model);

view.setPreferredSize(new Dimension(200,100));
frame.getContentPane().add(view);
frame.pack();
frame.setVisible(true);
assert (model.getSize() == 0);
controller.pause();

boolean toggle = model.toggle;
model.add("test0");
assert ( toggle == ! model.toggle);

controller.pause();
toggle = model.toggle;
model.add("test1");
assert ( toggle == ! model.toggle);

controller.pause();
assert(model.getSize() == 2);
view.setSelectedIndex(0);
assert(view.getSelectedValue().equals("test0"));

controller.pause();
toggle = model.toggle;
model.remove(0);
assert(toggle == ! model.toggle);

controller.pause();
assert(model.getSize() == 1);
view.setSelectedIndex(0);
assert(view.getSelectedValue().equals("test1"));

controller.pause();
System.exit(0);
}
}

class Model extends AbstractListModel {

boolean switch = false;

private Vector elements = new Vector();

...

public void fireIntervalAdded(AbstractListModel m, int start, int end) {
super.fireIntervalAdded(m,start,end);
this.switch = ! this.switch;
}

public void fireIntervalRemoved(AbstractListModel m, int start, int end) {
super.fireIntervalAdded(m,start,end);
this.switch = ! this.switch;
}

}

通過使用 setSelectedIndex() 和 getSelectedIndex(),我們對程序進行測試,測試雖只是稍有不同,但卻有極大改善。修改后的測試不單檢查模型,還檢查視圖,而且不只簡單地檢查行的數量,還檢查所選行的內容。

直接核查視圖的另一種方法是使用 Java Robot 類(在 Java 1.3 API 中有介紹 ? 請參閱參考資料),使 GUI 的鼠標和鍵盤的物理操作真正實現自動化。

Robot 類還答應您對屏幕的局部進行快照,答應建立基于 GUI 視圖實際物理布局的測試。當然,假如視圖的物理布局不如其邏輯結構穩定,這種能力會成為一種缺點。每次物理布局改變時,都必須重寫幾個測試是很痛苦的。因此,對于視圖不會頻繁改變的成熟的 GUI,我推薦您使用 Robot 類作為測試工具。要測試邏輯方面的問題,您可以象我們上面所做的那樣,調用視圖的方法。

最后一條忠告:對視圖對象中簡單地把調用彈回到模型的方法要小心。這樣做很快就會引入 Liar View。非凡是 JTables,其中包含有很多這樣的方法。

總結
以下是本周討論的錯誤模式的小結:

模式:Liar View
癥狀:GUI 程序通過了測試套件的測試,但后來卻顯示出本該在那些測試中被消除了的行為。
起因:測試只在模型方面做檢查,而沒有在視圖方面做檢查。
治療及預防措施:在視圖方面做檢查。

一點一點地,我們終于努力學完了關于最常見(也最讓人沮喪)的錯誤模式的解決方案,但我們仍然有很多事情要做。下次,我們將對付一種更具破壞性的錯誤:破壞者數據,數據一開始是完美的……直到存取它們時。且待下回分解!

參考資料

Dr. Dobbs Journal 在 1997 年就 Java 平臺上的 GUI 測試寫了一篇雖顯陳舊但卻很好的論文: “Automated testing is as problematic as it is essential”。
CSST Technologies, Inc 的 Web 站點主辦有一個很好的關于 GUI 測試中的一些問題的論壇。
請查閱 JUnit 并通過使您的代碼“布滿測試”以捕捉更多錯誤。
請訪問模式主頁,以獲得有關設計模式及其使用方法的具體介紹。
假如您還沒這樣做,請務必到 Java 1.3 API 查閱 Robot 類。
Malcolm Davis 的文章“Incremental development with Ant and JUnit”(developerWorks,2000 年 11 月)和 Jeff Canna 的文章“Testing, fun? Really?”(developerWorks,2001 年 3 月)為日常開發工作的測試提供了一些指導。
Malcolm Davis 寫的“Struts,一個開放源代碼的 MVC 實現”從另一個角度考察了模型-視圖-控制器體系結構。
閱讀 Eric 關于錯誤模式的完整系列。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 衡阳市| 钟祥市| 育儿| 九寨沟县| 乐亭县| 镇雄县| 浑源县| 枣庄市| 长宁区| 商丘市| 郁南县| 那曲县| 商城县| 雷波县| 新和县| 武宁县| 万载县| 临颍县| 嘉黎县| 蒙山县| 佳木斯市| 大城县| 汉中市| 剑川县| 吐鲁番市| 左云县| 佳木斯市| 拉萨市| 五华县| 芒康县| 福泉市| 湖口县| 吉木乃县| 安义县| 广平县| 长垣县| 周口市| 泽普县| 新乡市| 中方县| 新巴尔虎右旗|