技术开发 频道

使用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
相关文章