"Timeout"在测试框架里是如何被实现的

今天组里的小伙伴问了我一个问题:“我这里有一个底层驱动的接口,我想在测试它的时候加上超时限制,时间一过就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
时间: 2024-10-19 06:03:36

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

在测试框架中使用Log4J 2

之前的测试框架:http://www.cnblogs.com/tobecrazy/p/4553444.html 配合Jenkins可持续集成:http://www.cnblogs.com/tobecrazy/p/4529399.html 打log是一个测试框架必备的功能之一,trace测试执行的内容,使测试分析更容易和有规律可循.进而进一步处理测试log,实现自动分析测试结果. 现在java项目写日志一般使用Log4j 2 log4j 2是一个开源的记录log的框架,比log4j 效率更高 更多

前端测试框架 puppeteer 文档翻译

puppeteer puppeteer 是一个通过DevTools 协议提供高级API 来控制 chrome,chromium 的 NODE库; puppeteer默认运行在 headless 模式, 也可配置后运行在全模式(non-headless). puppeteer可以做什么 大部分在浏览器里手动执行的动作都可以通过puppeteer实现! 这里有几个列子来让你开始. 生成页面截图和PDF. 爬取单页面应用生成提前渲染的内容(例如 SSR). 自动提交表单, UI测试, 键盘输入等. 创

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

一.软件测试 1.软件测试的概念及分类 软件测试是使用人工或者自动手段来运行或测试某个系统的过程,其目的在于检验它是否满足规定的需求或弄清预期结果与实际结果之间的差别.它是帮助识别开发完成(中间或最终的版本)的计算机软件(整体或部分)的正确度 .完全度和质量的软件过程. 软件测试过程: 2.软件测试的分类 按是否关心软件内部结构和具体实现角度来分: 黑盒测试(Black-box Testing) 黑盒测试也称功能测试,测试中把被测的软件当成一个黑盒子,不关心盒子的内部结构是什么,只关心软件的输入

测试框架mochajs详解

测试框架mochajs详解 章节目录 关于单元测试的想法 mocha单元测试框架简介 安装mocha 一个简单的例子 mocha支持的断言模块 同步代码测试 异步代码测试 promise代码测试 不建议使用箭头函数 钩子函数 钩子函数的描述参数 异步的钩子函数 全局钩子 延迟启动测试 测试用例TODO 仅执行一个用例集/用例 跳过哪些用例集/用例 重新执行用例 动态生成用例 测试时间 测试超时 用例集执行超时 用例执行超时 钩子函数超时 diff差异比较功能 mocha使用命令和参数 mocha

手工测试 测试框架?如何提高测试效率?

百度了一下“测试框架”,搜索结果大部分都是“自动化测试框架”.“单元测试框架”,没有手工测试框架.但是所谓框架不就是把“共性部分形成的体系”提高效率和质量吗? 做测试3年,现在想的更多的是如何提高测试效率和保证测试用例的覆盖率.目前所在的是公司是互联网公司(之前一直在传统软件公司工作),节奏很快,测试周期很短.产品需求文档的完善程度也是参差不齐,然后测试时间又比较紧急,除了个别庞大的项目外,领导不会专门预留编写测试用例的时间. 事件一,2015/12/8,领导安排我和另外一个同事测试一个新增节点

selenium测试框架使用xml作为对象库

之前已经写过一篇: selenium测试框架篇,页面对象和元素对象的管理 上次使用的excel作为Locator对象管理,由于excel处理不够方便,有以下缺点: 不能实现分page 加载Locator对象 不能够实现Locator对象重名 文件比较大,读写速度没有xml快 所以,重新写了使用dom4j操作xml,使用xml管理Locator对象,能够有效解决以上问题 首先,定义Locator文件 <?xml version="1.0" encoding="UTF-8&

selenium 测试框架中使用grid

之前的测试框架:http://www.cnblogs.com/tobecrazy/p/4553444.html 配合Jenkins可持续集成:http://www.cnblogs.com/tobecrazy/p/4529399.html 在测试框架中使用Log4J 2 :http://www.cnblogs.com/tobecrazy/p/4557592.html 首先介绍一下grid ,selenium grid 是一种执行测试用例时使用的包含不同平台(windows.Linux.Androi

【夯实PHP基础】PHPUnit -- PHP测试框架

本文地址 分享提纲: 1.概述 2.安装 3.编写第一个测试用例 4.PHPUnit高级 5.参考 1.概述 1)[测试框架] 它是一款轻量级的PHP测试框架,是一个xUnit的体系结构的单元测试框架.复杂的项目,通过单元测试能够快速排查bug,有效减少bug的产生.简单的项目,使用php自带的var_dump().print_r()也能很方便的调试bug.PHPUnit通过运行测试用例里的断言(例如判断返回结果不为空),检查代码是否符合预期. 2.安装 安装方式有两种.一种是使用phar包,一

selenium测试框架篇,页面对象和元素对象的管理

前期已经做好使用Jenkins做buildhttp://www.cnblogs.com/tobecrazy/p/4529399.html 做自动化框架,不可避免的就是对象库. 有一个好的对象库,可以让整个测试体系: 更容易维护 大大增加代码重用 增加测试系统的稳定性 这里先了解一下我所说的对象库: 所谓的页面对象,是指每一个真是的页面是一个对象. 比如zhihu的登陆页面是一个页面对象,http://www.zhihu.com/#signin 这个页面对象主要包含一个输入邮箱的输入框(一个元素对