清单一: 实现代码中的主要属性
//单件模式,用来储存截图的缓冲区。
private static Vector bufferVec = null;
//标识缓冲区是否储存固定数目的截图,默认为固定数目。
private boolean isLimitedBuffer = true;
//伪视频所覆盖的自动测试过程,单位为秒;默认情况下存储最近30秒内的截图。
private int videoLength = 30;
//截图的时间间隔,单位为秒;默认情况下每隔1秒执行一次抓屏操作。
private double snapInterval = 1;
//缓冲区容量,该属性值由videoLength和snapInterval共同决定。
private int bufferCapability;
//伪视频输出位置
private String outputLocation = "c:/";
//伪视频格式,即生成的动态图片格式,在本例给出的实现使用了GIF格式。
private String pseudoVideoFormat = ".gif";
//标识生成的伪视频是否循环播放,默认为只播放一次。
private boolean replayIndefinitely = false;
//伪视频的重放速度,可选值为1,2,3。分别表示快速、中速和慢速。
private int replaySpeed = 2;
//标识是否有错误发生。当其值为true时,表明需要立即生成伪视频。
public static boolean errorFlag = false;
清单二: 实现代码中的主要方法
/* |
ScreenshotsPool方法为构造器,按照给定的参数对必要的属性进行初始化,并初始化唯一的缓冲区。
/* * * Get buffer pool that store screenshots * * @return Vector - the only buffer pool to store screenshots */ public Vector getBufferVec() { if(null==bufferVec) bufferVec = new Vector(bufferCapability); return bufferVec; } |
getBufferVec()方法是单件模式,用来获得唯一的截图缓冲区;如果缓冲区暂时还不存在,则进行初始化。
/* * Push a new screenshot into buffer * if the buffer is limited and has reached its capability, the oldest * snapshot would be removed */ public void pushScreenshot() { //获得当前屏幕的截图对象 BufferedImage image = getCurrentScreen(); if(null==image) return; //如果截图缓冲区有固定容量, if(isLimitedBuffer){ //如果已经到达缓冲区的最大容量,则删去第一副截图 int size = getBufferVec().size(); if(size==bufferCapability) getBufferVec().remove(0); } //将当前截图存入缓冲区 getBufferVec().add(image); } |
pushScreenshot方法用来将当前屏幕的截图存入缓冲区内。如果截图数目到达缓冲区上限,最早的截图会被删除。
/* * Get current screenshot * @return BufferedImage - screenshot of current screen */ public BufferedImage getCurrentScreen() { try{ //获得屏幕大小 Dimension screenSize = toolkit.getDefaultToolkit().getScreenSize(); int width = screenSize.width; int height = screenSize.height; BufferedImage capture = null; //获得当前屏幕范围 Rectangle area = new Rectangle(0, 0, width, height); Robot robot = new Robot(); //截取当前屏幕范围内的内容,返回BufferedImage对象 capture = robot.createScreenCapture(area); return capture; } catch (Exception e) { e.printStackTrace(); return null; } } |
getCurrentScreen方法用来获得当前屏幕的截图,返回值是一个包含当前屏幕图像的BufferedImage对象。
/* * Generate pseudo-video with those buffered screens in pool * Here we will generate animated picture of '.gif' format, and we can * generate animated picture of other formats likewise.(SVG, PNG, etc.) */ public void generatePseudoVideo() { int size = this.getBufferVec().size(); if(size<1) return; try { BufferedImage bi = null; //新建一个AnimatedGifEncoder对象,用来生成动态的GIF文件 AnimatedGifEncoder pseudoVideoGenerator = new AnimatedGifEncoder(); //设置GIF文件是否循环显示各帧 int repeat = replayIndefinitely?0:1; pseudoVideoGenerator.setRepeat(repeat); //设置输出位置,以当前时间为文件名 pseudoVideoGenerator.start(outputLocation+getCurrentTime()+pseudoVideoFormat); //依次将缓冲区内的所有截图按指定间隔加入AnimatedGifEncoder对象 for(int i=0;i<size;i++) { bi = (BufferedImage)(getBufferVec().get(i)); pseudoVideoGenerator.setDelay(200*replaySpeed); pseudoVideoGenerator.addFrame(bi); } //生成动态的GIF文件 pseudoVideoGenerator.finish(); } catch(Exception e) { e.printStackTrace(); } } |
generatePseudoVideo方法将缓冲区内的截图集中起来,每副截图作为一帧加入到一个生成器,再对相关参数做必要的设置,最后导出到一个GIF文件中。
其中,AnimatedGifEncoder类是一段开源程序,用来将一帧或多帧图像转换为一个GIF文件。作者是Kevin Weiner。
/** * main method of this thread */ public void run() { while(true) { try { //将当前屏幕的截图存入缓冲区 pushScreenshot(); //睡眠一段时间 Thread.sleep(new Double(1000*this.snapInterval).longValue()); //查看错误标识,一旦有错 if(errorFlag) { //立即生成伪视频 generatePseudoVideo(); //复位错误标识 errorFlag = false; } } catch(Exception e) { e.printStackTrace(); } } } |
run()方法是该线程的主要方法,通过轮询方式监测错误标识。如果在自动测试脚本在运行过程中有错误发生,它会将错误标识设为true;run()方法侦测发现该标识后,在第一时间内生成伪视频,再对错误标识进行复位。
五、总结
新的日志方案在原有方案的基础上进行了重构,对错误后截图的功能加以萃取和改进,使原有日志系统在不追加任何软硬件投资的前提下,能生成具有类似功能的伪视频。测试人员能够直观地了解自动测试过程,快速定位测试脚本或软件缺陷导致的错误成因,从而高效地优化测试脚本或填写测试报告。
本例给出的实现使用了GIF格式的动态图片作为生成的伪视频,读者可以按照类似方法,生成诸如SVG,PNG等其它格式的动态图片来再现测试过程。
对于新日志系统中的各种各样的表现形式:文本日志、截图、以及伪视频,可以使用HTML格式统一展现,有概况有明细,通过超链接将众多日志内容组织在一起,让日志系统能更加友好方便地供测试人员查阅。