基于JavaScript的Web应用自动化测试

分享于 

17分钟阅读

Web开发

  繁體

Background

我一直在寻找用 C# 开发的web应用程序的自动测试工具。 我试图找到一些工具,但有些是复杂的,有些是付费的,有些不允许我调整。 我有一些是 below,但我想要的是总控制,非常轻松轻松便携。

我的灵感:

  • http://watin.sourceforge.net
  • http://sahi.co.in/w/
  • http://seleniumhq.org/projects/ide/

解决方案

所以,finally 回到我的旧的JavaScript技能,我最终创建了一个简单的( 希望有效) 自动测试工具 ! 这个想法是创建一个包装父网页,它将在一个iframe中测试网页,然后访问它的控件,填充值,执行点击,然后执行点击。

开始测试按钮

<input type="button"value="Start Testing" onclick="javascript:TC =
'EmptySearch';doTesting(false);"/>

iframe事件( 在 postback 后继续测试)

<divstyle="text-align: center"><iframesrc="../Forms/SearchUser.aspx"id="ifrmUserSearch"name="ifrmUserSearch"width="96%"height="500px"onload="doTesting(true);"/></div>

*NOTE: 两个页面都需要在同一台计算机上进行测试( 不允许跨域测试)

 ma8a6.gif

下面是一个说明我的方法的示例图像:

下面是代码的包装页面:

1.<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">2.<html xmlns="http://www.w3.org/1999/xhtml">3.<head>4. <title>Test User Search </title>5.<scripttype="text/javascript"src="../Testing.js"></script>6.<scripttype="text/javascript">7.var TC = "";8.var uName = "admin"; var userRoleDDLIndex1 = 1; 
 var userRoleDDLIndex2 = 2; /* zero-based */9.var email = "";10. 11.var ifrm = document.getElementById("ifrmUserSearch");12.var doc = getDocument("ifrmUserSearch"); 13. 14.var lnkbtnSort1=""; var txtNameFind=""; var ddltxtNameFind="";15.var lnkbtnSort2=""; var ddlOrgFind=""; var lnkbtnUserRole="";16.var ddlUserRoleFind =""; var lnkbtnEmail=""; var txtEmailFind="";17.var ddltxtEmailFind ="";18. 19.var lnkbtnAddManage ="";var btnSearch="";var btnResetSearch="";20.var ibtnFirst="";var ibtnPrevious="";var ibtnNext="";var ibtnLast="";var ddlPages="";21. 22.var chkStop = "";var chkTC01 = "";var chkTC02 = "";
 var chkTC03 = "";var chkTC04 = "";var chkTC05 = "";23.var txtLog = "";24. 25.//HT:***IMP: In pages with AJAX postbacks (i.e. ALL the pages)the iframe's //"onload" is never called.26.// So need to add a statement: if(window.parent!= null) window.parent.doTesting(true);27.// In the function EndRequestHandler(sender, args) (i.e. Ref Search.aspx, line: 1828.function doTesting(isResult)29.{30. setVariables(); 31. 32. if(chkStop.checked) return;//STOP if checkbox is checked!33. 34. //IE calls onload 'before' loading and mozilla does it 'after'35.// http://maniish.wordpress.com/2006/12/22/documentreadystate-alternative-in-mozilla/#comment-198936. if(doc.readyState && doc.readyState!="complete") return; 37. var skipNext = false;38. 39. switch (TC)40. { 41. case"EmptySearch":42. if(isResult)43. {chkTC01.checked = false; EmptySearch(true);}44. else45. {46. if(chkTC01.checked) EmptySearch(false);47. else {skipNext = true; TC = "HeaderSorting1";}48. }49. break;50. case"HeaderSorting1": 51. if(isResult)52. {HeaderSorting1(true);TC = "HeaderSorting2";}53. else54. {55. if(chkTC02.checked) HeaderSorting1(false);56. else {skipNext = true;TC = "PaginationNxt";}57. }58. break;59. case"HeaderSorting2": 60. if(isResult)61. {chkTC02.checked = false; HeaderSorting2(true);}62. else63. {64. if(chkTC02.checked) HeaderSorting2(false);65. else {skipNext = true;TC = "PaginationNxt";}66. }67. break;68. /* Start : PAGINATION Test Cases*/69. /* Special case - need to recurse for other paginations (so set TC)*/70. case"PaginationNxt": 71. if(isResult)72. {Pagination(true,"Next"); TC = "PaginationPrev";}73. else74. {75. if(chkTC03.checked) Pagination(false,"Next");76. else {skipNext = true;TC = "FullSearch";}77. }78. break;79. case"PaginationPrev": 80. if(isResult)81. {Pagination(true,"Prev"); TC = "PaginationLast";}82. else83. {84. if(chkTC03.checked) Pagination(false,"Prev");85. else {skipNext = true;TC = "FullSearch";}86. }87. break;88. case"PaginationLast": 89. if(isResult)90. {Pagination(true,"Last"); TC = "PaginationFirst";}91. else92. {93. if(chkTC03.checked) Pagination(false,"Last");94. else {skipNext = true;TC = "FullSearch";}95. }96. break;97. case"PaginationFirst": 98. if(isResult)/* Full Search will be continued */99. {Pagination(true,"First");TC = "PaginationGoto";}100. else101. {102. if(chkTC03.checked) Pagination(false,"First");103. else {skipNext = true;TC = "FullSearch";}104. }105. break; 106. case"PaginationGoto": 107. if(isResult)/* Full Search will be continued */108. {chkTC03.checked = false; Pagination(true,"Goto");}109. else110. {111. if(chkTC03.checked) Pagination(false,"Goto");112. else {skipNext = true;TC = "FullSearch";}113. }114. break; 115. /* End : PAGINATION Test Cases*/116. case"FullSearch": 117. if(isResult)118. {chkTC04.checked = false; FullSearch(true);}119. else120. {121. if(chkTC04.checked) FullSearch(false);122. else {skipNext = true;TC = "RandomSearch";}123. }124. break;125. case"RandomSearch": 126. if(isResult)127. {chkTC05.checked = false; RandomSearch(true);}128. else129. {130. if(chkTC05.checked) RandomSearch(false);131. else {skipNext = true;TC = "ResetSearch";}132. }133. break;134. case"ResetSearch": 135. if(isResult)136. {ResetSearch(true); TC = ""; alert("User Search Testing complete");}137. else {ResetSearch(false);}138. break;139. }140. //Testing continues if it was just a result or we need to skip to the next test-case141. //HT: MAKE SURE TC is set or it'll be an infinite loop!!!142. if(isResult || skipNext && TC!= "")doTesting(false);143.}144. 145.function FullSearch(done) 146.{147. if(done) {alert("Result:TC04: Search for : Name(admin), 
 Role(Admin) & Email(admin@cclear.com)"); return;}148. 149. alert("Search:TC04: Full Search");
 //Need onchange to set value!150. txtNameFind.value = uName; txtNameFind.onchange();
 //Need onchange to set value!151. ddlUserRoleFind.selectedIndex = userRoleDDLIndex1; ddlUserRoleFind.onchange();152. txtEmailFind.value = email; txtEmailFind.onchange();//Need onchange to set value!153. btnSearch.click();154.}155. 156.function RandomSearch(done) 157.{158. if(done) {alert("Result:TC05: Search for : Role(User)"); return;}159. 160. alert("Search:TC05: Random Search");
 //Need onchange to set value!161. txtNameFind.value = ""; txtNameFind.onchange();
 //Need onchange to set value!162. ddlUserRoleFind.selectedIndex = userRoleDDLIndex2; ddlUserRoleFind.onchange();163. txtEmailFind.value = ""; txtEmailFind.onchange();//Need onchange to set value!164. btnSearch.click();165.}166. 167.function setVariables()168.{ 169. ifrm = document.getElementById("ifrmUserSearch"); 170. doc = getDocument('ifrmUserSearch'); 171. if(doc == null)return;172. 173. /* Name column */174. lnkbtnSort1 = doc.getElementById("ctl00_cphChild_gvListUser_ctl01_lnkbtnName");175. txtNameFind = doc.getElementById("ctl00_cphChild_gvListUser_ctl01_txtNameFind");176. ddltxtNameFind = doc.getElementById("ctl00_cphChild_gvListUser_ctl01_ddltxtNameFind");177. /* Org column */178. lnkbtnSort2 = doc.getElementById("ctl00_cphChild_gvListUser_ctl01_lnkbtnOrg");179. ddlOrgFind = doc.getElementById("ctl00_cphChild_ddlOrgFind");180. /* UserRole column */181. lnkbtnUserRole = doc.getElementById("ctl00_cphChild_gvListUser_ctl01_lnkbtnUserRole");182. ddlUserRoleFind = doc.getElementById("ctl00_cphChild_gvListUser_ctl01_ddlUserRoleFind");183. /* Email column */184. lnkbtnEmail = doc.getElementById("ctl00_cphChild_gvListUser_ctl01_lnkbtnEmail");185. txtEmailFind = doc.getElementById("ctl00_cphChild_gvListUser_ctl01_txtEmailFind");186. ddltxtEmailFind = doc.getElementById("ctl00_cphChild_gvListUser_ctl01_ddltxtEmailFind");187. 188. //HT: The following IDs work only for Pagesize:4 (total records: 10)189. ibtnFirst = doc.getElementById("ctl00_cphChild_gvListUser_ctl05_ibtnFirst");190. ibtnPrevious = doc.getElementById("ctl00_cphChild_gvListUser_ctl07_ibtnPrevious");191. ibtnNext = doc.getElementById("ctl00_cphChild_gvListUser_ctl07_ibtnNext");192. ibtnLast = doc.getElementById("ctl00_cphChild_gvListUser_ctl07_ibtnLast");193. 194. ddlPages = doc.getElementById("ctl00_cphChild_gvListUser_ctl07_ddlPages");195. 196. commonSetVariables();// set common controls197.}198. </script>199. 200.</head>201.<body style="font-family: Verdana; font-size: 12px; margin: 1px 1px 1px 1px;">202. <h3>203. UserSearch Automated Testing</h3>204. 205. <div style="position:absolute;top:10px;left:500px;"> <a href="../TestingSiteMap.html">Testing Sitemap</a></div>206. <div style="vertical-align: middle">207. [<input type="checkbox" id="chkTC01" name="chkTC01" 
 checked="checked"/>: TC01:Empty Search]<br/>208. [<input type="checkbox" id="chkTC02" name="chkTC02" 
 checked="checked"/>: TC02:Header Sorting]<br/>209. [<input type="checkbox" id="chkTC03" name="chkTC03" 
 checked="checked"/>: TC03:Pagination (Next, Prev, First, 
 Last & Goto Page] Note: IDs tested for Pagesize:4 (total records: 10)<br/>210. 211. [<input type="checkbox" id="chkTC04" name="chkTC04" 
 checked="checked"/>: TC04:Full Search]<br/>212. [<input type="checkbox" id="chkTC05" name="chkTC05" 
 checked="checked"/>: TC05:Random Search]<br/>213. 214. <input type="button"value="Start Testing" onclick="javascript:TC = 
 'EmptySearch';doTesting(false);"/>215. [<input type="checkbox" id="chkStop" name="chkStop"/>Stop]216. <br/>217. <br/>218. <!--<textarea id="txtLog" rows="5" cols=""></textarea>-->219. 220. <div style="text-align: center">221. <iframe src="../../Forms/Search/SearchUser.aspx" id="ifrmUserSearch" 
 name="ifrmUserSearch"222. width="96%" height="500px" onload="doTesting(true);"/>223. </div>224. </div>225.</body>226.</html>

逻辑和流( 它如何递归循环)

因为这是"你自己的代码",将有一个时间设置代码,需要一些努力。 比如,

function setVariables()

你将需要为用于测试的子页( 要测试的页面) 中的每个控件声明变量。 你将需要查看呈现页和从它的获取控件的控件 id。 这样就可以在代码逻辑中直接访问控件。

function doTesting(isResult)

这是调用'递归地'的主函数,直到标记代码中的标志以通知测试完成( 例如 )。 TC ="",isResult 参数用于分隔这里函数的服务器 postback 调用。 在这个函数中,一些初步检查有一个does语句,它确定这个执行在哪个测试用例中。

switch (TC)
 { 
 case"EmptySearch":
 if(isResult)
 {chkTC01.checked = false; EmptySearch(true);//Tested, show notification}else {
 if(chkTC01.checked) EmptySearch(false); // perform test//set test-case variable & skip to next }else {skipNext = true; TC = "HeaderSorting1";
 }
 break;

在每种情况下,流如下所示:

  • 每个测试用例都有一个函数,如函数 EmptySearch(done)
  • If isResult = true - 意味着测试已经进行,所以我们取消选中复选框,并使用标记done=true调用测试用例函数
  • 如果测试用例还未执行,那么首先我们检查用户是否已经检查了测试用例。 然后我们调用测试用例例程。
  • 注意:此时发生的事情非常棘手,因此请注意。 现在从测试用例例程可以触发一个 postback,这意味着refreshed页面被刷新。 在服务器响应到达之前,脚本执行不能"等待",直到我们在- EmptySearch(false); 之后退出函数
  • 诀窍是捕获执行流并将它的返回到下一步。 这里有两个步骤:
    • 测试用例是这样做的,。 当流退出开关时,我们有另一个递归,确保触发下一个测试用例。 这就是:
    • //Testing continues if it was just a result or we need to skip to the next test-case//HT: MAKE SURE TC is set or it'll be an infinite loop!!!if(isResult || skipNext && TC!= "")doTesting(false);
  • 这将将流返回到开关案例中的相同测试用例,这次执行以下代码:
  • else {skipNext = true; TC = "HeaderSorting1"; //set test-case variable & skip to next
  • 和 finally,我们回到递归,这次与 skipNext = true; 和一个新的测试用例集 TC ="HeaderSorting1";
  • 这个递归将流返回到开关情况,但这一次是在下一个例子中重复的,直到所有测试用例完成。
  • 对于复杂的逻辑,但这是唯一的方法,我可以找到继续流并且保持通知之间的通知。 但是一旦你理解了你将知道它的强制性的机制。 当然,我对那些能够在任何形式优化我的代码的逻辑高手都很。
  • 目前,在 Testing.js 文件中,我尝试将一些常用函数捆绑起来,因此如果你没有看到同一文件中的所有函数,那么就不要惊慌

我希望你可以不用解释如何设置控制值,然后触发按钮点击事件。 我已经实现/测试了基于AJAX的web应用程序,这样这样就能够简单地用于那些普通 postback web应用。

高级:对于基于AJAX的网络应用程序,你需要添加的是一个回调到页面顶部的doTesting(true);,如下所示:

$(document).ready(function(){if(window.parent
!= self) window.parent.doTesting(true);}

结束语

我开发了这种方法,因为我需要一些自由的tweak-n-tap,并且允许完全自由。 由于now-a-days网络应用程序越来越复杂,工具很酷,但有时它们限制了功能。 除了递归逻辑之外,它的余的代码很简单,它更容易调整测试用例。添加。编辑和删除测试用例。 我希望它有帮助。

如果有人有兴趣,我也发布了一个收费的视频,展示了它的实际运行情况: 基于简单JS的自动测试用户搜索。

我想知道你的建议和想法,以及你喜欢什么。 我看到这种方法的许多用法,如自动测试,显示一个自动演示,automated-data-entry。 但是同一个领域的一切。


WEB  JAVA  Javascript  BASE  AUTO  test