如何在代码层实现可测试性-以《热词分析》代码为例
系统的质量属性包括六类,分别是:可用性、可修改性、性能、安全性、可测试性、易用性。其中可测试性是非执行就可见的质量因素。是指通过测试揭示软件缺陷的难易程度,如果在软件中有错误,可测试性就表示软件在下次运行时不能正常运行的可能性。例如《热词分析》的程序,因为我写的《热词分析》程序是java与微信小程序的结合,其中就涉及到了数据、端口号等的问题,不知不觉中可能就会出现错误,可测试性的响应度量处理的是测试在发现缺陷方面的效率以及想要达到某个期望的覆盖范围。需要用多长时间进行测试。
可测试性是代码编写中非常重要的部分,不仅在编写代码时需要进行测试,在程序设计和架构时也需要测试。人们越来越想要提高代码的测试效率,测试效率越低意味着在在开发时的成本越高,如何降低测试成本使得可测试性技术效率更高开始被人们所关注。可测试性技术涉及多个方面,其最终目标是提高软件的质量,降低全寿命周期费用。目前可测试性技术研究主要集中在面向软件代码的可测试性度量上。通过了解,Voas 等[78)并给出了一种基于错误1失效模型的软件可测试性度量计算方法,即PIE(Propagation& Infection&Execution)技术, Jin-Cherng Lin 等人侧提出了-种基于静态检测代码的可测试性度量计算方法,Pu-lin Yeh等人(6l提出了基于数据流的可测试性度量计算方法。这些度量计算方法都是面向程序代码。 一个好的代码离不开测试,作为软件本身的一种特性,软件可测试性应该具有如下特征:
(1)可观察性。可观察性好的软件产品,使得被测对象和测试结果易被观察和跟踪,信息掩盖率低,错误揭示率高;在《热词分析》代码中代码功能明确,爬取,清洗,传递,展示,都容易被观察追踪,且代码都有注释
public static void sample(AipNlp client) { String text = "热词分析"; // 传入可选参数调用接口 HashMap<String, Object> options = new HashMap<String, Object>(); // 词法分析 JSONObject res = client.lexer(text, options); System.out.println(res.toString(2)); }
(2)可操作性。如果设计的软件很少存在BUG,在进行测试时的效率就会很高。
(3)可控制性。可控制性好的软件产品,能够从软件产品的输入来控制它的各种输出,软和硬件状态和变量能直接由测试工程师进行控制,使得软件的自动测试工作变得更容易;换句话说就是软件本身接受定义明确的参数,并且这些参数可由测试者灵活的传入,软件在接受到这些参数后通过一系列运算返回固定的结果。在写《热词分析》的程序时,要应该清楚的表明自己需要什么参数,以及将会生成什么返回值。
public static void sample(AipNlp client) { String text = "热词分析"; // 传入可选参数调用接口 HashMap<String, Object> options = new HashMap<String, Object>(); // 词法分析 JSONObject res = client.lexer(text, options); System.out.println(res.toString(2)); }
(4)易理解性。软件的设计在逻辑上和操作上易于理解;
(5)可分解性。软件可以分解为多个独立的或弱相关的模块,提高软件模块测试的独立性:
(6)简单性。软件在满足需求的基础,上要尽量简单。
有的代码是不好测试的,在进行测试时,会发现程序中某些部分很难进行自动测试,比如耦合程度比较高的类、用户界面、数据库、Servlets和EJB类、等等。首先是不知道测什么,其次是一些代码之间互相依赖严重,在测试环境中要建立起这些类的实例都很难。
1、高类聚、低耦合:要在代码层实现可测试性,我们需要做的就是把我们自己写的代码部分和这些代码从物理上尽量的分离开,这样一来我们写的代码就可以测试了。至于Servlets这样的程序,可以使用HttpUnit或者使用一段程序调用其中的业务代码进行测试。我们还要减少类之间的耦合,如果类之间高耦合,那么我们在测试一个类时,同时也需要测试与他相耦合的类,这是非常麻烦的。这也是我们在设计模式中学到的知识,高类聚低耦合。在《热词分析》中,尽量把相关的功能放在一个类,一个类尽量只负责完成一个职责,并且类与类之间要关联的越少越好,这对于代码的可修改性和可测试性以及复用性都是非常有意义的。为了减少类之间的耦合,我们可以进入接口,一个类可以不与其他类耦合,而直接调用这个接口,就可以工作了。在测试时实现一个虚拟的接口,这样就可以很方便的对类进行测试了。要提高代码层的可测试性,同时也要求程序具有较好的层次性,用户界面和其背后的业务逻辑是分离的,用户界面与其他的逻辑界面的耦合是非常低的,模块间的联系非常是非常松散的,这样在测试时不会出现相互感染,用户界面和逻辑界面可以单独进行测试,一些部分也可以单独进行测试。程序设计一般遵循层次原则,一般说来,上层的程序可以调用下层的程序,同层之间也可能存在互相的调用。一个较好的层次结构代表的是,层次之间原则上坚持单向调用,如果存在层次之间的双向调用,那就破坏了层次原则,在测试时也会变得比较麻烦,程序的可复用性也会降低。
class T{ privateString id; publicvoidsetId(String id) { this.id=id; } publicString getId() { return id; } } class S{ private String id; public void setId(String id) { this.id=id; } public String getId() { return id; } } class SM{ publicList<S> getAllS() { List<S> list=newArrayList<S>(); for (int i=0;i<100;i++) { S s=new S(); s.setId("S的编号是"+i); list.add(s); } return list; } public void printAllS() { List<S>list1=this.getAllS(); for (S s:list1) { System.out.println(s.getId()); } } } class TM { publicList<T> getAllT() { List<T> list=newArrayList<T>(); for (inti=0;i<100;i++) { T t =new T(); t.setId("T的编号"+i); list.add(t); } return list; } publicvoidprintAllT() { List<T> list2=this.getAllT(); for (T t:list2) { System.out.println(t.getId()); } } } public classClient { publicstaticvoidmain(String[] args) { TM=newTM(); tm.printAllT(); SM=newSM(); sm.printAllS(); } }
上面代码的几个类每个类都执行单一的职责,类与类之间互相没有联系,是高类聚低耦合的表现。
2、记录回放:也就是将捕获的代码写入日志文件中,方便进行数据的对比,以方便进行测试,查找错误。
String path = "D:\\rizhi.log";//日志文件 File file =new File(path); FileWriter fw; try { fw = new FileWriter(file,true); BufferedWriter bw = new BufferedWriter(fw); String s1 = String.valueOf(discountFactor); String s2 = Arrays.toString(cashFlow); bw.write(s1+"\t"+s2+"\r"); bw.flush(); bw.close(); } catch (IOException e) { e.printStackTrace(); }
//将捕获的信息写入日志文件
通过bw.write(s1+"\t"+s2+"\r");将捕获的代码写入日志文件,在之后测试时,就可以通过与日志文件中的数据进行对比而得出数据是否错误。
3、输出完全依赖于传递的输入,这样代码就很容易测试了
@Test public void getTimeOfDayTest() { Calendar time = GregorianCalendar.getInstance(); time.set(2018, 10, 1, 06, 00, 00);//设置数据得输入,通过检测输出得数据来进行测试 String timeOfDay = GetTimeOfDay(time); Assert.assertEquals("Morning", timeOfDay); }
如果代码的输出可以完全依赖输入,那么在进行测试时,只需要对代码进行输入操作,查看输出的结果是否是我们想要的,就可以很方便的得到结果。
4、实时监察,及时提醒:可以随时检查代码此时的状态,出现错误时及时出现提醒
package com.bwie.test; /* * 自定义异常 必须继承Exception 或者其子类 * */ public class MyException extends Exception { public MyException(String message) { super(message); } public MyException(){ } //测试方法 public static void main(String[] args) { MyException test=new MyException(); try{ test.createDoubleArray(-10); }catch(MyException ex){ ex.printStackTrace(); } } //自定义异常方法 public double[] createDoubleArray(int length)throws MyException{ if(length<0){ throw new MyException("数组的长度不能小于0"); } return new double[length]; } }
通过自定义异常的方式可以得到随时进行测试。