当前位置: 首页 > 工具软件 > busted > 使用案例 >

lua开源测试框架busted源码学习(-)----outputHandlers模块

谢灵均
2023-12-01

项目中用到了lua的单元测试框架busted,根据需求需要对busted源码进行分析,这次主要分析一下outputHandlers模块。

项目的地址见:点击打开链接

默认busted的输出见runner.lua:如下两行代码。

options = tablex.update(require 'busted.options',options or {})
options.output = options.output or (isatty and 'utfTerminal' or 'plainTerminal')

options是命令行读进来的一些参数。如果命令行参数为空,options.output  则会返回(isatty and 'utfTerminal' or 'plainTerminal')

isatty简单来说就是检测给定的文件描述符是否链接到一个终端,因为lua中的io库中io.stdin、io.stdout以及io.stderr是默认提供的预定义的句柄,所以isatty为true,那么最终的options.output就会取utfTerminal或者plainTerminal。

tablex.updata可以在 /usr/local/share/lua/5.3/pl/tablex.lua中看到,其实就是表拷贝的过程,tablex.lua的位置可以root  用find 命令查找;

function tablex.update (t1,t2)
    assert_arg_writeable(1,t1)
    assert_arg_iterable(2,t2)
    for k,v in pairs(t2) do
        t1[k] = v
    end
    return t1
end
当然,如果加了 -o  /busted/outputHandlers/xxx.lua,那么在output_handler_loader.lua中会调用:

handler = require('busted.outputHandlers.' .. output)
这样最终会按照读取的格式进行显示输出。


outputHandlers目录下最重要的文件就是base.lua,其他就是不同输出格式的lua文件,包括gtest.lua、json.lua、junit.lua、plainTerminal.lua、sounds.lua、TAP.lua以及utfTerminal.lua。虽然格式文件多,但是查看代码,都包括以下三部分内容:

1.busted和handler的引入

local busted = require("busted")
local handler = require("busted.outputHandler.base")()

2.定制自己的输出函数

handler.testStart = function(element, parent) 
 -----xxxxxxx
end
handler.testEnd = function(element, parent, status, trace)
--xxxxxxxxxxxxxx
end

3.subscribe自定义的函数

busted.subscribe({'test', 'start'}, handler.testStart)
busted.subscribe({'test', 'end'}, handler.testEnd)

这样就可以完成定制格式的输出;

其实outputHandlers模块有策略模式的影子,base.lua可以看成是strategy基类,gtest.lua等可以看成是不同的策略实现,”继承”于base.lua。base.lua中需要根据格式进行重写的方法包括:

busted.subscribe({ 'suite', 'reset' }, handler.baseSuiteReset, { priority = 1 })
busted.subscribe({ 'suite', 'start' }, handler.baseSuiteStart, { priority = 1 })
busted.subscribe({ 'suite', 'end' }, handler.baseSuiteEnd, { priority = 1 })
busted.subscribe({ 'test', 'start' }, handler.baseTestStart, { priority = 1, predicate = handler.cancelOnPending })
busted.subscribe({ 'test', 'end' }, handler.baseTestEnd, { priority = 1, predicate = handler.cancelOnPending })
busted.subscribe({ 'pending' }, handler.basePending, { priority = 1, predicate = handler.cancelOnPending })
busted.subscribe({ 'failure', 'it' }, handler.baseTestFailure, { priority = 1 })
busted.subscribe({ 'error', 'it' }, handler.baseTestError, { priority = 1 })
busted.subscribe({ 'failure' }, handler.baseError, { priority = 1 })
busted.subscribe({ 'error' }, handler.baseError, { priority = 1 })

可以查看busted.subcribe,在core.lua文件中

function busted.subscribe(...)
    return mediator:subscribe(...)
end
继续跟进在term/mediator.lua中可以看到mediator:subscribe的代码,其实就是在callback中添加回调函数(这块函数后续将继续分析)

subscribe = function(self, channelNamespace, fn, options)
        return self:getChannel(channelNamespace):addSubscriber(fn, options)
end,
addSubscriber = function(self, fn, options)
      local callback = Subscriber(fn, options)
      local priority = (#self.callbacks + 1)

      options = options or {}

      if options.priority and
        options.priority >= 0 and
        options.priority < priority
      then
          priority = options.priority
      end

      table.insert(self.callbacks, priority, callback)

      return callback
end,

重写了之后,运行时调用的就是定义的方法而不是base.lua中的基类方法,这也可以看成是一种继承;

base.lua中提供了一个handler表,记录测试中出现的失败、错误以及成功等的信息,持有的方法包括:

handler.cancelOnPending   
handler.subscribe
handler.getFullName
handler.format
handler.getDuration
handler.baseSuiteStart
handler.baseSuiteReset
handler.baseSuiteEnd
handler.baseTestStart
handler.baseTestEnd
handler.basePending
handler.baseTestFailure
handler.baseTestError
handler.baseError   

其中handler.format方法即将每个用例的信息进行格式化的一个过程,format中持有一个表formatted

 local formatted = {
      trace = debug or element.trace,
      element = copyElement(element),
      name = handler.getFullName(element),
      message = message,
      randomseed = parent and parent.randomseed,
      isError = isError
 }
这张表会保存每个element的信息,包括状态、调试信息、名称等;然后根据测试结果的不同将formatted表insert到不同的表中,最后再做不同的处理。

table.insert(handler.failures, handler.format(element, parent, message, debug))
table.insert(handler.errors, handler.format(element, parent, message, debug, true))
table.insert(insertTable, formatted)


上述是看代码过程中的一点笔记,也还存在一些不太懂的地方,需要结合busted源码其他的文件进行深入;









 类似资料: