今天组里的小伙伴问了我一个问题:“我这里有一个底层驱动的接口,我想在测试它的时候加上超时限制,时间一过就fail掉它,执行后面的测试用例。怎么办到呢?”。我问:“它自己没有超时响应的机制么? 超时抛exception或者返回错误提示什么的?”,小伙伴回答是“好像没有。” 我接着问: “这个接口是做什么的,是核心交易么?” “算是吧,调用还挺频繁的。”小伙伴回答。“那这个接口决不能让它通过测试啊!”我大声回答,旁边n人侧目。“好吧。那我如何实现超时fail呢?” 小伙伴继续问。。。“呃。。。让我慢慢道来。”

超时处理其实是编程过程中经常要面对的问题。在我们调用某个函数的时候,调用方把控制权交给了被调用方,但被调用方很多时候是不可控的,如果被调用方长时间不给调用方返回结果,调用方就要想别的办法,不然就hang死在那里了。这就是超时处理最初始的需求,它的本质是要求把同步调用变成异步调用。把同步变异步其实是个较大的话题,不同的高级语言,框架,甚至操作系统都进行了各式各样的封装,提供了各式各样的接口,十分精彩,但这并不是今天的重点,因此不会展开说了。下面举几个例子来看看“timeout”是怎么实现的。先拿JAVA语言来说吧:

首先要说明的是,在单线程下,不借助一些特殊工具,“超时处理”是很难实现的,请见下面的代码:

RemoteServer itest = new RemoteServer()
String result = itest.callRemote() //callRemote()是一个远端接口

如果callRemote()方法永远不给返回值,那程序就一直停留 result = itest.callRemote() 这一行不往下走了。

如何实现下面语法中想要的结果呢,如果callRemote()永远不会抛出TimeoutException的话?

try{
RemoteServer itest = new RemoteServer()
     result = itest.callRemote()
}catch(TimeoutException e) 
{ e.printStackTrace() }

多线程?这是个好主意!你最初的想法可能是:让一个子线程在调用前开始计时,如果超时了通知主线程。如果是我,我会一般想到两种通知的方式:一种是抛出异常,让主线程捕获,另一种是Listener的方式实现callback。

如果你使用第一种抛异常的方式,见如下代码:

public class TimeCount implements Runnable {
private long timeOut;
private long beginTime;
public TimeCount(long timeOut,long beginTime){
this.timeOut = timeOut;
this.beginTime = beginTime;
} public void run() throws TimeOutException{
while(true) {
if((System.currentTimeMillis()-beginTime)>timeOut){
throw new TimeOutException("Timeout!");
}
}
}
}

恭喜你,Java不允许run() 方法向上抛出异常。就算你@override 它也不行。这也是JDK早期的线程模型一个重要槽点。

OK,那我只能使用listener的方式了。

Private XXListener lstr;  

  public void run() throws TimeOutException{
while(true) {
if((System.currentTimeMillis()-beginTime)>timeOut){
lstr.notify("Time out!");
}
}
}

但是这样如果没有现成的Listener,你就要去实现它,还是很复杂的(有兴趣可以看看这篇文章),同时,对被测类产生了一定入侵。可见,上面两种方法都不是什么好方法。那么有没有什么较好的方法呢?Java其实对多任务调度实现了非常好的封装在(java.util.concurrent包里),我们可以使用下面代码方便的实现异步。

先看一下被测类:

import java.util.*;
public class TimeOutCall {
public String CallWithTimeOut( long timeSetting ) //被测物方法,输入参数可以设置多长时间返回。
{
long startTime = System.currentTimeMillis ();
while(true )
{
if(System.currentTimeMillis() - startTime < timeSetting )
continue;
else
return "Result returned!" ;
}
}
}
 
 
 

再看一下实现超时的调用类:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class CallTest {
public static void main(String [] args)
{
ExecutorService service = Executors. newSingleThreadExecutor();
Callable<String> callable = new Callable<String>(){ //实现Callable接口的匿名类
public String call() throws Exception{
TimeOutCall toc = new TimeOutCall();
return toc.CallWithTimeOut(3000); //3秒返回结果
}
};
Future<String> future = service.submit( callable);
service.shutdown();
try {
if(service.awaitTermination (1000, TimeUnit.MILLISECONDS) == false)//等待1秒后抛出异常。
throw new TimeoutException();
System. out.println(future .get()); } catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

上述代码中我们创建了一个ExecutorService类,并且将一个Callable接口类型的作业提交给了类,而Future可以异步的等待Callable作业的执行结果。想查看作业执行时间的话,ExecutorService提供了一个相当方便的方法 awaitTermination来检测是否作业在开始后一段时间还在继续执行,通过对方法第一个参数的设置,我们很容易能够设置超时的时限。

事实上,Junit也是用类似的方法实现超时检测的,在Junit中,我们可以方便的使用Annotation给一个测试方法加入超时检测:

@Test(timeout =1000)
public void testXXX(){
...
}

而它在代码中对方法超时的实现核心代码如下(org.junit.internal.runners.MethodRoadie.java中):

 private void runWithTimeout(final long timeout) {
runBeforesThenTestThenAfters(new Runnable() { public void run() {
ExecutorService service = Executors.newSingleThreadExecutor();
Callable<Object> callable = new Callable<Object>() {
public Object call() throws Exception {
runTestMethod();
return null;
}
};
Future<Object> result = service.submit(callable);
service.shutdown();
try {
boolean terminated = service.awaitTermination(timeout,
TimeUnit.MILLISECONDS);
if (!terminated) {
service.shutdownNow();
}
result.get(0, TimeUnit.MILLISECONDS); // throws the exception if one occurred during the invocation
} catch (TimeoutException e) {
addFailure(new TestTimedOutException(timeout, TimeUnit.MILLISECONDS));
} catch (Exception e) {
addFailure(e);
}
}
});
}

不过,如果你在Junit中使用了@BeforeClass 和@AfterClass,并且有多个测试用例都必须检测超时,则建议使用Rules来设置整体超时时间。

大家也可以参考StackOverFlow上的这个链接,看一下讨论过程,相信会有一个更加深入的理解:

http://stackoverflow.com/questions/2758612/executorservice-that-interrupts-tasks-after-a-timeout

上文说过,不同语言,不同操作系统,timeout实现起来很不一样。比如python,如果在UnixLike系统下,可以用python内置的signal包来方便的实现超时,我们来看下面的例子:

import signal, os

def handler(signum, frame): #产生超时后调用。
print 'Signal handler called with signal', signum
raise IOError("Couldn't open device!") # Set the signal handler and a 5-second alarm
signal.signal(signal.SIGALRM, handler) #指定handler
signal.alarm(5) #设置5秒超时 # This open() may hang indefinitely
fd = os.open('/dev/ttyS0', os.O_RDWR) signal.alarm(0)

在使用的时候,我们可以很方便的把它也封装成Decorator(等同于Java的Annotation)

但是,在Windows下,就没有那么幸运了,因为signal包直接使用了unixlike操作系统的信号量机制,这时候实现超时就会相对麻烦一些。什么?多线程?答案又一次对了。我们可以使用multiprocessing 

包中的函数来实现超时检测。下面是粗略的代码实现(真的是粗略的实现)要想深入了解,请看python多线程的在线文档

__author__ = 'lucasliu'
from multiprocessing import Process
import time def timefucntion(sleeptime ):
time.sleep(sleeptime)
print 'timefuction returned after', sleeptime,'seconds' if __name__ == '__main__':
p = Process(target=timefucntion,args=(3,)) starttime = time.time()
timeoutsetting = 2 p.start()
p.join(timeout=timeoutsetting)
if abs(time.time() - starttime - timeoutsetting)<0.1:
print 'timeout'
p.terminate()

那么在常见python的测试框架里,timeout又是如何实现的呢?有点儿遗憾,python的内置单测框架unittest不支持超时检测。因此我们来看Robotframework是如何实现超时的:一句话,就是根据不同的操作系统,用不同的方法实现超时,并在框架上层统一起来,对用户透明。源码量稍微有点儿大,就不在这里搬运了。有兴趣可以去看Robotframework的 robot.running.timeouts包里的代码,看完一定会有收获。

至于ruby,就封装的更好了。直接有一个timeout库,引入后可以极为方便的实现timeout,如下面代码:如果do sothing的时间超过了 timeoutsetting的设置就会抛出异常。所以,用ruby的同学相对幸福一些。

require 'timeout'

begin
timeout(timeoutsetting ){
do something
}
rescue Exception
puts "timeout"
ensure
puts "finish"
end

"Timeout"在测试框架里是如何被实现的的更多相关文章

  1. 在测试框架中使用Log4J 2

    之前的测试框架:http://www.cnblogs.com/tobecrazy/p/4553444.html 配合Jenkins可持续集成:http://www.cnblogs.com/tobecr ...

  2. NodeJs下的测试框架Mocha

    介绍和代码下载 Mocha在2011年发布,是目前最为流行的javascript框架之一,在本文我们重点介绍它在NodeJs上的使用. 如果你需要下载实例代码,可以通过这个链接 gitClone 或者 ...

  3. Java高级特性 第11节 JUnit 3.x和JUnit 4.x测试框架

    一.软件测试 1.软件测试的概念及分类 软件测试是使用人工或者自动手段来运行或测试某个系统的过程,其目的在于检验它是否满足规定的需求或弄清预期结果与实际结果之间的差别.它是帮助识别开发完成(中间或最终 ...

  4. BDD测试框架Spock概要

    前言 为了找到一个适合自己的.更具操作性的.以DDD为核心的开发方法,我最近一直在摸索如何揉合BDD与DDD.围绕这个目标,我找到了Impact Mapping → Cucumber → Spock ...

  5. 前端测试框架 puppeteer 文档翻译

    puppeteer puppeteer 是一个通过DevTools 协议提供高级API 来控制 chrome,chromium 的 NODE库; puppeteer默认运行在 headless 模式, ...

  6. [转]Python测试框架对比----unittest, pytest, nose, robot framework对比

      测试框架 什么是框架? 框架(Framework)是整个或部分系统的可重用设计,框架是用来解决代码的组织及运行控制问题的. 在我们编写自动化脚本的时候,经常需要读取配置文件,读取数据文件,发送请求 ...

  7. Python测试框架对比

    如有任何学习问题,可以添加作者微信:lockingfree 更多学习资料请加QQ群: 822601020获取 unittest, pytest, nose, robot framework对比 什么是 ...

  8. Python测试框架对比----unittest, pytest, nose, robot framework对比

    什么是框架? 框架(Framework)是整个或部分系统的可重用设计, 框架是用来解决代码的组织及运行控制问题的. 在我们编写自动化脚本的时候,经常需要读取配置文件,读取数据文件,发送请求,记录日志, ...

  9. EPF:一种基于进化、协议感知和覆盖率引导的网络协议模糊测试框架

    本文系原创,转载请说明出处:from 信安科研人 目录 实验 工具的安装 1.安装AFL++ 2.安装epf 对IEC104协议库进行fuzz 实验准备 使用AFL++中的编译器插桩 开始fuzz 原 ...

随机推荐

  1. JavaScript Patterns 4.9 Configuration Objects

    Configuration Objects Passing a large number of parameters is not convenient. A better approach is t ...

  2. python之递归实现

    一.递归函数 概念:递归算法是一种直接或者间接的调用自身算法的过程.在计算机编写程序中,递归算法对解决一大类问题是十分有效的. 特点: ①递归就是在过程或者函数里调用自身. ②在使用递归策略时,必须有 ...

  3. json数据处理实战:Kafka+Flume+Morphline+Solr+Hue数据组合索引

    背景:Kafka消息总线的建成,使各个系统的数据得以在kafka节点中汇聚,接下来面临的任务是最大化数据的价值,让数据“慧”说话. 环境准备: Kafka服务器*3. CDH 5.8.3服务器*3,安 ...

  4. W3School-CSS测验

    The only way to survive was to enjoy the good moments and not dwell too much on the bad. 生活,就应该享受美好的 ...

  5. 初涉Linux ----------> 打造自己的 Vim IDE

    一.  开篇前言 (图片显示越界的话,请刷新) 装好Ubuntu15.04系统之后呢,玩了玩 Ubuntu,感觉还是很不错的.比windows快,一开机就可以打开你想要的程序,但是在windows下你 ...

  6. Java报表FineReport在医院院长查询分析系统中有什么用

    1.医院院长查询系统的价值 目前,大中型医院的信息处理正从传统手工方式飞速向电脑信息化建设方案转变,一个大中型医院担负着繁重的医疗和科研任务,以及繁杂的事务性工作,院长必须时刻与各科室保持密切的连续, ...

  7. [译] 企业级 OpenStack 的六大需求(第 1 部分):API 高可用、管理和安全

    全文包括三部分: 第一部分:API 高可用和管理以及安全模型 第二部分:开放架构和混合云兼容 第三部分:弹性架构和全球交付 引言 OpenStack 是构造企业级私有云的非常理想的基础.它立志成为新一 ...

  8. Google Cloud Platform

    一个离我们很遥远,很遥远的公司.作为全球三大公有云厂商之一,在国内根本听不到他的声音.其实吧,听到了也没用,因为在国内没法用!AWS还在纠结的落地过程中挣扎,GCP基本上就当不存在吧. 抛开这些乌烟瘴 ...

  9. [转]在ASP.NET开发中容易忽略的2个小问题 Cookie乱码存取异常 和 iframe弹框的login跳转

    本文转自:http://www.cnblogs.com/outtamyhead/p/3642729.html 本文地址:http://www.cnblogs.com/outtamyhead/p/364 ...

  10. MMORPG大型游戏设计与开发(概述)updated

    1.定义 MMORPG,是英文Massive(或Massively)Multiplayer Online Role-PlayingGame的缩写,即大型多人在线角色扮演游戏. 2.技术与知识 在这系列 ...