六大质量属性——可测试性代码层面描述(以“信息领域热词分析系统”为例)
先说一下可测试性:
可测试性战术的目标是允许在完成软件开发的一个增量后,轻松的对软件进行测试。
测试的目标是发现错误。
具有可测试性的代码有什么特点?
1. 控制性。
控制性是指测试者给在被测试的软件提供固定的输入数据的方便程度。换句话说就是软件本身接受定义明确的参数,并且这些参数可由测试者灵活的传入,软件在接受到这些参数后通过一系列运算返回固定的结果。任何软件都应该清楚的表明自己需要什么参数,以及将会生成什么返回值。此外任何软件都应该应该抽象它的依赖,譬如参数或底层模块,并为外部调用者提供随意注入的方式。当然软件代码本身应该清晰,整洁,目标明确.
2.可见性。
可见性是指测试者观察正在测试的软件的当前状态以及它所产生的任何输出的能力。换句话说就是软件应该将内部运算的状态(一般是指错误状态)和输出结果清晰明确的告知测试者。可见性一般都是通过方法执行后验证后置条件完成。
验证后置条件与契约式设计有关。所谓的契约式设计,是指把组件之间的交互描述成契约,权利和义务得到明确的表达和强制实施。在.net环境下,可以通过.net Framework4新增的Code Contracts库创建软件契约。
3.简约性。
一般而言,简约性对任何系统在任何情况下都是一个正面的属性,测试毫无疑问也不例外。简单和极其内聚的组件非常适合测试,因为他们暴露出来的方法少,需要的测试也就少,而需要的测试越少,就越能做得可靠,快速.
信息领域热词分析系统界面:
详见:热词分析系统
在此系统中最主要的功能便是,查找数据库中的热词,然后显示该热词的意思和博文的连接,当然还需要前期,爬取博客园博文,进行词频统计,进行百度百科释义,这些操作,由于这个庞大的系统是由多个子系统构成,所以在此就以最后这个展示窗口解释可测试性。
相对建民老师的课件,先对可测试性做出代码层面解答。
单一职责原则
模块化,高内聚,低耦合
单一职责原则是代码编写的灵魂,在我们编写这些代码的时候必须要遵守的准则,相交而言我认为数据库的增删改查完美地运用了这些单一职责原则,每一个函数进行数据库操作,对于后期代码的测试提升了很高的方便性。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public static ArrayList<InfoBean> select_baike_list( String table) 2 { 3 Connection conn = DBUtil.getConn(); 4 ArrayList<InfoBean> userBeans=new ArrayList<InfoBean>(); 5 InfoBean userBean; 6 try { 7 Statement state = conn.createStatement(); 8 ResultSet rs = state.executeQuery("select * from "+table+""); 9 while(rs.next()) { 10 //如果有结果,是认为是通过验证了 11 userBean = new InfoBean(); 12 userBean.setId(rs.getString("ID")); 13 userBean.setParent(rs.getString("Parent")); 14 userBean.setChild(rs.getString("Child")); 15 userBean.setUrl(rs.getString("Url")); 16 userBean.setNum(rs.getString("Num")); 17 userBeans.add(userBean); 18 } 19 } catch (Exception e) 20 { 21 e.printStackTrace(); 22 } 23 24 return userBeans; 25 } 26 27 28 public static String select_baike_blogs_ci( String name) 29 { 30 Connection conn = DBUtil.getConn(); 31 String str=""; 32 try { 33 Statement state = conn.createStatement(); 34 ResultSet rs = state.executeQuery("select * from word_info_blog where Name like '%"+name+"%' limit 1"); 35 while(rs.next()) { 36 //如果有结果,是认为是通过验证了 37 str=rs.getString("Url"); 38 } 39 } catch (Exception e) 40 { 41 e.printStackTrace(); 42 } 43 44 return str; 45 }
记录与回访
“撤销” 可看简书
撤销键Ctrl-z是后悔药、建民老师也常说人生没有后悔药,但是代码有,与其说后悔药,倒不如说是拿了一个小本本记下了所有的动作,记下了所有的日志、用空间换取了记忆,在这个代码里,主要是运用了链表的格式,对数据进行了记录。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * 在链表表尾插入一个结点 3 * 4 * @param data 5 */ 6 private void insertInTail(T data) { 7 Node<T> newNode = new Node<>(data); 8 // 保存为当前的节点 9 this.mCurrentNode = newNode; 10 if (mTail == null) { 11 // 为null,说明是尾节点 12 mHead = newNode; 13 mTail = newNode; 14 15 // 和头部相连接,形成环形双向链表 16 mTail.mNext = mHead; 17 mHead.mPrevious = mTail; 18 } else { 19 newNode.mPrevious = mTail; 20 mTail.mNext = newNode; 21 mTail = newNode; 22 23 // 和头部相连接,形成环形双向链表 24 mTail.mNext = mHead; 25 mHead.mPrevious = mTail; 26 } 27 }
管理输入输出
输入和输出是测试的时候的重要的两端,控制好了其中的一段,那么再代码设计、代码预期、代码结果就能更加直观的查找出来错误的根源,这是黑盒测试的根源,也是白盒测试中的主要观察点。
一、可测试性目标是发现错误,通过完成一个增量,就可以简单的检查出错误,那么在这个系统中就是输入这个增量,如果错误就应当提示无此内容,然而本系统显示
这就是一个错误,也便是管理输入输出中的双方出现了问题,相对之在servlet中的代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 private void Select(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 2 // TODO Auto-generated method stub 3 //response.getWriter().append("Served at: ").append(request.getContextPath()); 4 req.setCharacterEncoding("utf-8"); 5 resp.setContentType("text/html;charset=utf-8"); 6 resp.setCharacterEncoding("UTF-8"); 7 HttpSession session=req.getSession(); 8 PrintWriter out=resp.getWriter(); 9 System.out.println("AAAAAAAAAAAAA"); 10 //resp.sendRedirect(req.getContextPath() + "/right.jsp"); 11 12 String select_name = req.getParameter("select_name"); 13 InfoBean Bean=new InfoBean(); 14 Bean=InfoDao.select_baike_info("baike_list_num", select_name); 15 String url=""; 16 17 url=InfoDao.select_baike_blogs_ci(select_name); 18 if(!url.equals("")) 19 { 20 System.out.println("Name"); 21 System.out.println(url); 22 Bean.setUrl(url); 23 }else 24 { 25 url=InfoDao.select_baike_blogs_title(select_name); 26 if(!url.equals("")) 27 { 28 System.out.println("Title"); 29 System.out.println(url); 30 Bean.setUrl(url); 31 }else 32 { 33 url=InfoDao.select_baike_blogs_info(select_name); 34 if(!url.equals("")) 35 { 36 System.out.println("Info"); 37 System.out.println(url); 38 Bean.setUrl(url); 39 } 40 } 41 42 } 43 44 45 if(Bean!=null) 46 { 47 session.setAttribute("selectBean", Bean); 48 resp.sendRedirect(req.getContextPath() + "/admin/main.jsp"); 49 } 50 else 51 { 52 resp.sendRedirect(req.getContextPath() + "/select.jsp"); 53 } 54 55 }
针对req.getParameter("select_name");应作出相应的提示
接口分离
有了标准的接口,进行有效的隔离,能够极大程度的减少测试员的工作量,模块化测试,单元测试极大地减少了测试过程中用例的原则,如果没有接口隔离,不管用的是极值分析法还是等价划分法,都对测试员的工作造成了极大的负担,相对而言,单元测试更加简化了工作量,让白盒测试过程中的逻辑复杂度降低了不少。
通过使用ajax动态调取servlet,servlet再和dao层代码联系,最后实现数据库查询,操作,再将数据返回ajax
1 <script type="text/javascript"> 2 3 $.ajax({ 4 type : "post", 5 async : true, 6 url : "${pageContext.request.contextPath}/InfoServlet", 7 type:"POST", 8 data: 9 { 10 "method":"select" 11 }, 12 dataType:"json", 13 14 success:function(data) { 15 $.each(data,function(index) 16 { 17 $(".results").append("<blockquote>"+data[index].Name+"<cite> – "+data[index].Info+"</cite></blockquote>"); 18 }); 19 20 }, 21 error : function(errorMsg) { 22 //请求失败时执行该函数 23 alert("请求数据失败!"); 24 } 25 }); 26 </script>
特化访问路线接口
选取特定标准的接口,或者编写特定的接口,同样加强了高内聚低耦合的设计思路,以在架构的任何层次上应用测试用例,并且已经具备观察响应的测试功能
1.get请求
获取响应结果的response.status_code,response.text,response.headers,response.cookies等等县城的函数,具体可以查看Python的官方文档
2.post请求,入参有四种格式,这里主要说明常用的json格式
3,Servlet 中的 response request
不光servlet中有response和request jsp中也有,而且这样的思路涵括在代码编程中的很深,更像设计代码时候的初中,只有0 1 非正即否。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 private void select_child(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException, SQLException { 2 // TODO Auto-generated method stub 3 //response.getWriter().append("Served at: ").append(request.getContextPath()); 4 req.setCharacterEncoding("utf-8"); 5 resp.setContentType("text/html;charset=utf-8"); 6 HttpSession session=req.getSession(); 7 PrintWriter out=resp.getWriter(); 8 System.out.println("AAAAAAAAAAAAA"); 9 //resp.sendRedirect(req.getContextPath() + "/right.jsp"); 10 JSONArray array=new JSONArray(); 11 String Parent = req.getParameter("Parent"); 12 List<InfoBean> beans=new ArrayList<InfoBean>(); 13 beans=InfoDao.select_baike_list_child("baike_list", Parent); 14 int num=0; 15 for(InfoBean n:beans) 16 { 17 num++; 18 if(num>7) 19 { 20 break; 21 } 22 JSONObject job = new JSONObject(); 23 job.put("Child", n.getChild()); 24 array.put(job); 25 } 26 27 resp.setCharacterEncoding("UTF-8"); 28 System.out.println(array.toString()); 29 //resp.sendRedirect(req.getContextPath() + "/admin/child/Child_11/1_Rcai.jsp"); 30 resp.getWriter().write(array.toString()); 31 }
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #!/usr/bin/python3 2 import requests 3 import urllib3 4 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) 5 import _thread 6 import time 7 # 为线程定义一个函数 8 def addtask(): 9 for n in range(1,1001): 10 url ="https://xxxxxxxx.com/graphql" 11 taskname ="task" +str(n) 12 headers = { 13 "authorization":"xxxxxxxxxxxxxxxxxxxxxxxxxxx" 14 } 15 date = {"operationName":"addTask","variables": {"streamID":137,"name": taskname}, 16 "query":"mutation addTask($name: String!, $streamID: ID!) {\n createNewTask(input: {streamID: $streamID, name: $name}) {\n id\n name\n __typename\n }\n}\n"} 17 response = requests.request("post", url,headers=headers,json=date,verify=False) 18 # time.sleep(1) 19 # print(name) 20 print(response.text) 21 # 创建20个线程 22 try: 23 for nin range(1,21): 24 _thread.start_new_thread(addtask, ()) 25 except: 26 print ("Error: 无法启动线程") 27 while 1: 28 pass
二:内部监视:
内部监视器我的理解就是监视代码、事件的运行状况,每一个监视的初衷就是看这个代码在每一步中怎么运行的,同时可以看eclipse中的DeBug,那个就是监视功能在测试层面的巨好的使用,每一步骤都能看到输入的参数以及输出的值,如果有一个好的内部监视功能,那么会是一个很好的程序
主要是内部监视器,相对这种网页系统,更好的便是谷歌浏览器的监视功能。
同时也可以在代码中,设置一个try时时刻刻监视代码运行过程中动向,实时作出反应
任务管理器
任务 理器对资源进行管理,我们在代码设计中也可以对代码消耗的资源进行监视,例如:如果我的LIst链表数据太多,内存占了好多(当然arraylist是没有办法做到这样绝望的程度)那么我就可以及时运行数据转成txt文档的操作,让内存转化为硬盘存储,然后对文件进行调取(这或许就是数据库创建的时候的想法)那么我就可以吧内存消耗转化为硬盘消耗,在代码上获得了成功,而且我认为,将数据存到txt还会降低时间复杂度,一举两得。
内部监视器
内部监视器在本意上是监控程序遇到了哪一步骤,然后告知到达遇到这一步骤,然后就自动的执行这一步骤
代码 参考
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package listenertest3; 2 3 4 5 public class DownLoadEvent { 6 7 8 9 private String fileName; 10 11 private boolean isFinish = false; 12 13 14 15 public DownLoadEvent(String fileName) { 16 17 this.fileName = fileName; 18 19 } 20 21 22 23 public String getFileName() { 24 25 return fileName; 26 27 } 28 29 30 31 public void setFileName(String fileName) { 32 33 this.fileName = fileName; 34 35 } 36 37 38 39 public boolean isFinish() { 40 41 return isFinish; 42 43 } 44 45 46 47 public void setFinish(boolean isFinish) { 48 49 this.isFinish = isFinish; 50 51 } 52 53 }