使用MCP对Ajax应用程序进行自动测试
【IT168技术文档】
本文将使用Mozilla Control Program来对Ajax应用程序进行自动测试。
该测试用例对要进行测试的应用程序的设计和结构十分清楚,从某种角度上讲,它属于白盒测试,HTML的id和name值假定使知道的,哪种用户交互将会触发Ajax事务也在该测试用例中被假定为已知的。这些对该测试用例是有效的。
Listing 1: The setUp method
1private MCP mcp = null; 2
3
public CarDemoTest(String testName) ...{ 4
super(testName); 5
} 6
7
public void setUp() ...{ 8
super.setUp(); 9
10
mcp = new MCP(); 11
try ...{ 12
mcp.setAppData(getBrowserBinDir()); 13
} 14
catch (Exception e) ...{ 15
fail(); 16
} 17
18
}
【IT168技术文档】
上述代码清单摘自JUnit,包括Webclient的可执行版,清单1展示了构造器,setUp方法,测试的一个私有实例变量,MCP实例。setUp方法调用mcp.setAppData(),这个重要的方法告诉了Webclient哪里可以找到本地浏览器的二进制(该例中Xulrunner)。如果Webclient不能被合适的进行初始化,该测试用例是失败的。如在代码15行。
清单2:实际的testCarDemo()单元测试
Listing 2: The testCardDemo method
1public void testCardemo() throws Exception ...{ 2
mcp.getRealizedVisibleBrowserWindow(); 3
final BitSet bitSet = new BitSet(); 4
AjaxListener listener = new AjaxListener() ...{ 5
public void endAjax(Map eventMap) ...{ 6
bitSet.flip(TestFeature.RECEIVED_END_AJAX_EVENT.ordinal()); 7
if (null != eventMap) ...{ 8
bitSet.flip(TestFeature.HAS_MAP.ordinal()); 9
} 10
// Make some assertions about the response text 11
String responseText = (String) eventMap.get("responseText"); 12
if (null != responseText) ...{ 13
if (-1 != responseText.indexOf("<partial-response>") && 14
-1 != responseText.indexOf("</partial-response>")) ...{ 15
bitSet.flip(TestFeature.HAS_VALID_RESPONSE_TEXT.ordinal()); 16
} 17
} 18
Document responseXML = (Document) 19
eventMap.get("responseXML"); 20
Element rootElement = null, element = null; 21
Node node = null; 22
String tagName = null; 23
try ...{ 24
rootElement = responseXML.getDocumentElement(); 25
tagName = rootElement.getTagName(); 26
if (tagName.equals("partial-response")) ...{ 27
element = (Element) rootElement.getFirstChild(); 28
tagName = element.getTagName(); 29
if (tagName.equals("components")) ...{ 30
element = (Element) rootElement.getLastChild(); 31
tagName = element.getTagName(); 32
if (tagName.equals("state")) ...{ 33
bitSet.flip(TestFeature. 34
HAS_VALID_RESPONSE_XML.ordinal()); 35
} 36
} 37
} 38
} 39
catch (Throwable t) ...{ 40
41
} 42
43
String readyState = (String) eventMap.get("readyState"); 44
bitSet.set(TestFeature.HAS_VALID_READYSTATE.ordinal(), 45
null != readyState && readyState.equals("4")); 46
bitSet.flip(TestFeature.STOP_WAITING.ordinal()); 47
48
} 49
}; 50
mcp.addAjaxListener(listener); 51
52
// Load the main page of the app 53
mcp.blockingLoad("http://javaserver.org/jsf-ajax-cardemo/faces/chooseLocale.jsp"); 54
// Choose the "German" language button 55
mcp.blockingClickElement("Germany"); 56
// Choose the roadster 57
mcp.blockingClickElement("roadsterButton"); 58
// Sample the Basis-Preis and Ihr Preis before the ajax transaction 59
Element pricePanel = mcp.findElement("zone1"); 60
assertNotNull(pricePanel); 61
String pricePanelText = pricePanel.getTextContent(); 62
63
assertNotNull(pricePanelText); 64
assertTrue(pricePanelText.matches("(?s).*Basis-Preis\\s*15700.*")); 65
assertTrue(pricePanelText.matches("(?s).*Ihr Preis\\s*15700.*")); 66
67
// Choose the "Tempomat" checkbox 68
bitSet.clear(); 69
mcp.clickElement("cruiseControlCheckbox"); 70
71
while (!bitSet.get(TestFeature.STOP_WAITING.ordinal())) ...{ 72
Thread.currentThread().sleep(5000); 73
} 74
75
// assert that the ajax transaction succeeded 76
assertTrue(bitSet.get(TestFeature.RECEIVED_END_AJAX_EVENT.ordinal())); 77
assertTrue(bitSet.get(TestFeature.HAS_MAP.ordinal())); 78
assertTrue(bitSet.get(TestFeature.HAS_VALID_RESPONSE_TEXT.ordinal())); 79
assertTrue(bitSet.get(TestFeature.HAS_VALID_RESPONSE_XML.ordinal())); 80
assertTrue(bitSet.get(TestFeature.HAS_VALID_READYSTATE.ordinal())); 81
bitSet.clear(); 82
83
// Sample the Basis-Preis and Ihr-Preis after the ajax transaction 84
pricePanel = mcp.findElement("zone1"); 85
assertNotNull(pricePanel); 86
pricePanelText = pricePanel.getTextContent(); 87
88
assertNotNull(pricePanelText); 89
assertTrue(pricePanelText.matches("(?s).*Basis-Preis\\s*15700.*")); 90
assertTrue(pricePanelText.matches("(?s).*Ihr Preis\\s*16600.*")); 91
92
mcp.deleteBrowserControl(); 93
}
【IT168技术文档】
第二行引起实际的浏览器窗口关闭和打开,因为MCP使用java.awt.Robot来模拟鼠标和键盘的输入,这种模型也就意味着当运行测试用例的时候不能使用计算机。第三行创建了一个final BitSet实例,该方法可以处理浏览器的交互,该交互要求一个从浏览器到客户应用程序之间的回调。还有一个内部类,他是AjaxListener的一个实例,如4-49行所示。
AjaxListener有三个方法被测试覆盖,endAjax, errorAjax和startAjax。当浏览器成功完成一个Ajax事务时(Ajax Response code 4)endAjax被调用,当Ajax事务异常中止时,errorAjax被调用。当浏览器内的代码在XmlHttpRequest实例中调用send时,startAjax被调用。三个方法通过映射,该映射包括请求的信息。映射的内容根据被覆盖的方法而不同。
startAjax:
映射包括下列值
headers
所有请求的java.util.Map.
method
本事件请求的方法.
readyState
XMLHTTPRequest readyState的数字字符串
endAjax 和 errorAjax :
映射将包括下列值
method
本事件的请求方法
responseXML
响应XML的org.w3c.dom.Document实例
responseText
a String instance of the response Text.
status
从服务器响应的状态字符串像"200 OK".
headers
所有响应头的java.util.Map
method
本事件的请求方法
readyState
XMLHTTPRequest readyState的数字字符串
在Line 6,我们使用了一个特殊的enum,TestFeature在BitSet内定义了少量的任务为测试的不同断言,Line 6的flip表示Ajax的事务最终是成功的,Line8的flip表示,MCP确实为该事务提供了一个映射,Line 15表示XmlHttpRequest的responseText是按照期望出现的,Lines 24-34使用W3C DOM API来检测响应的XML和验证是否与预期的相符,Line 44 flip表示readState的值也是正确的,Line 46表示正在进行Ajax检测,这对后面是很重要的。
从内部类出来,在Line 50简单的增加了ajaxListener实例。
Line 53使MCP加载给定的URL,知道加载完成时进行阻塞,Line 55使MCP点击id或name为Germany的按钮,并等待加载完成,HTML的id属性值在一个页面内是唯一的,name属性的值并不需要唯一,因此,如果MCP没有找到给定的id,则开始找第一个与name属性相匹配的元素。Line 57选择Roadster。
在Line 59-65,页面从Roadster得到价格信息,使用Java的正则表达式来判断基本的价格和你的价格是否都是15700,Line 68使我们清除了点击复选框前的bitset,该复选框引起Ajax事务,在该例子中,点击复选框表明用户想增加cruise control到他们的车上。Line 69确实点击了,这次没有使用blocking click 方法。Line 71-73使用了一个无穷的循环来睡眠5秒钟。
Lines 84-90检查价格来验证价格在选择巡游控件后上升了。
MCP提供简单的API对于测试Ajax应用是足够的,更复杂的测试将使用Webclient API。
0
相关文章
