用破坏测试lua脚本

时间:2014-04-30 01:11:03

标签: unit-testing lua freeswitch lua-busted

我试图用破坏的方式测试我们的Freeswitch lua脚本,并且遇到了麻烦。它的要点是我需要能够监视代码,如下所示

local req_host = session:getVariable('sip_req_host')
session:setVariable('curl_timeout', 0)

但我似乎无法弄清楚如何构建我应该设置_G.session的对象。我可以找到的最好/唯一的好例子是https://github.com/chris-allnutt/unit-tested-corona/blob/master/mocks/button.lua,但似乎使用相同的简单语法来构建被破坏的文档所做的模拟对象。

local button = {
  x = 0,
  y = 0,
  addEventListener = function() end
}

我可以看到这对于不需要返回任何东西的简单函数是如何工作的,但我需要能够使用getVariable和setVariable函数在会话对象中获取和设置变量。我的简单模拟对象如下:

Session = {}
Session.__index = Session

function Session.create(params)
  local session = {}
  setmetatable(session, Session)
  session.params = params
  return session
end

function Session:getVariable(key)
  return self.params[key]
end

function Session:setVariable(key, val)
  self.params[key] = val
end

function Session:execute(cmd, code)
end

,测试如下

require "busted"
require("test_utils")

describe("Test voip lua script", function()
  it('Test webrtc bad domain', function()
    domain = 'rtc.baddomain.com';
    session_params = {['sip_req_host'] = domain,
                      ['sip_req_user'] = 'TEST-WebRTC-Client',
                      ["sip_from_user"] = 'testwebrtc_p_12345',
                      ['sip_call_id'] = 'test@call_id',
                      ['sip_authorized'] = 'false'}
    exec_str = 'sofia_contact TEST-WebRTC-Client@'..domain;
    api_params = {[exec_str] = 'error/user_not_registered'}

    _G.session = mock(Session.create(session_params), 'execute')
    _G.api = API.create(api_params)
    _G.freeswitch = Freeswitch.create()

    dofile("tested_script.lua")

    assert.spy(_G.session.execute).called_with("respond", "407")
  end)
end)

我最终遇到以下异常。     /usr/local/share/lua/5.2/luassert/spy.lua:78:尝试索引函数值

luassert抛出了这个异常,这是一个被破坏的库的依赖,在下面的if语句中

77:local function called_with(state, arguments)
78:  if rawget(state, "payload") and rawget(state, "payload").called_with then
79:    return state.payload:called_with(arguments)
80:  else
81:    error("'called_with' must be chained after 'spy(aspy)'")
82:  end
83:end

我对lua很陌生,所以我似乎只是错过了一些明显的语言部分,但是我们将非常感谢任何帮助或指示。

3 个答案:

答案 0 :(得分:1)

FreeSWITCH中的

mod_lua使用稍微定制的Lua解释器,您似乎使用安装在主机上的不同Lua解释器。我猜他们不会轻易合作。

答案 1 :(得分:1)

所以我在调试的另一天之后找到的答案是肯定的,你确实需要使用一个表作为你调用mock的对象。但是,因为在构建具有可调参数的对象时,lua是一种非常宽容的语言,所以这仍然最终起作用。由于与此问题无关的原因,我在对象周围构建了一个包装器,但您可以看到我最终在下面工作的内容。

function SessionConstructor.create(params)
  local session_constructor = {}
  setmetatable(session_constructor, SessionConstructor)
  session_constructor.session = {}
  session_constructor.session.params = params
  session_constructor.session.getVariable = function(self,key)
    return self.params[key]
  end
  session_constructor.session.setVariable = function(self, key, val)
    self.params[key] = val
  end
  session_constructor.session.execute = function(self, cmd, code)
  end

  return session_constructor
end

function SessionConstructor:construct()
  return self.session
end

一个重要的警告,因为你必须将自己传递给将被lua""调用的函数:"语法中监视调用的函数的方法确实发生了变化,如下面的测试文件中所示。

require "busted"
require "test_utils"

describe("Test voip lua script", function()
  it('Test webrtc bad domain', function()
    domain = 'rtc.baddomain.com';
    session_params = {['sip_req_host'] = domain,
                      ['sip_req_user'] = 'TEST-WebRTC-Client',
                      ["sip_from_user"] = 'testwebrtc_p_12345',
                      ['sip_call_id'] = 'test@call_id',
                      ['sip_authorized'] = 'false'}
    local sess_con = SessionConstructor.create(session_params)

    exec_str = 'sofia_contact TEST-WebRTC-Client@'..domain;    
    local api_con = APIConstructor.create()
    api_con:expect_exec(exec_str, 'error/user_not_registered')

    _G.session = mock(sess_con:construct())
    _G.api = mock(api_con:construct())
    _G.freeswitch = create_freeswitch()

    dofile("tested_script.lua")

    assert.spy(session.execute).was.called_with(session, "respond", "407")
    assert.spy(session.execute).was_not.called_with("respond", "407") --This is unfortunate
  end)
end)

答案 2 :(得分:0)

我对busted bin脚本进行了一些逆向工程,并得到了以下脚本(我们称之为runner.lua):

busted = require 'busted.core'()
local environment = require 'busted.environment'(busted.context)

function unpack(t, i)
  i = i or 1
  if t[i] ~= nil then
    return t[i], unpack(t, i + 1)
  end
end
busted.getTrace = function(element, level, msg)
    level = level or  3

    local info = debug.getinfo(level, 'Sl')
    info.traceback = debug.traceback('', level)
    info.message = msg
    if msg ~= nil then
      freeswitch.consoleLog("NOTICE", msg)
    end

    local file = busted.getFile(element)
    return file.getTrace(file.name, info)
end

busted.safe = function(descriptor, run, element, setenv)
    if setenv and (type(run) == 'function' or getmetatable(run).__call) then
      -- prioritize __call if it exists, like in files
      environment.wrap(getmetatable(run).__call or run)
    end

    busted.context.push(element)
    local trace, message

    local ret = { xpcall(run, function(msg)
      message = busted.rewriteMessage(element, msg)
      freeswitch.consoleLog("ERR", message)
      trace = busted.getTrace(element, 3, msg)
    end) }

    if not ret[1] then
      busted.publish({ 'error', descriptor }, element, busted.context.parent(element), message, trace)
    end

    busted.context.pop()
    return unpack(ret)
end
require 'busted.init'(busted)

local checkTag = function(name, tag, modifier)
  local found = name:find('#' .. tag)         
  return (modifier == (found ~= nil))         
end                                           

local checkTags = function(name)              
  for i, tag in pairs(tags) do                
    if not checkTag(name, tag, true) then     
      return nil, false                       
    end                                       
  end                                         

  for i, tag in pairs(excludeTags) do         
    if not checkTag(name, tag, false) then    
      return nil, false                       
    end                                       
  end                                         

  return nil, true                            
end          

local getTrace =  function(filename, info)      
  local index = info.traceback:find('\n%s*%[C]')
  info.traceback = info.traceback:sub(1, index) 
  return info, false                            
end                                             

local file = setmetatable({
  getTrace = getTrace
}, {
  __call = loadfile("/path/scripts/main_spec.lua")
})
busted.executors.file("main_spec.lua", file)

local failures = 0                      
local errors = 0                        

busted.subscribe({ 'error' }, function()
  errors = errors + 1                   
end)                                    

busted.subscribe({ 'test', 'end' }, function(element, parent, status)
  if status == 'failure' then                               
    failures = failures + 1                                 
  end                                                       
end)                                                        

busted.publish({ 'suite', 'start' })
busted.execute()
busted.publish({ 'suite', 'end' })
freeswitch.consoleLog("NOTICE", "Failures: " .. failures)
freeswitch.consoleLog("NOTICE", "Errors: " .. errors)

该脚本仅针对一个文件/path/scripts/main_spec.lua,但仍可使用。 您可以使用此runner.lua脚本执行的操作是使用来自Freeswitch控制台的luarun运行它:

fs_cli
luarun /path/to/runner.lua

你会在那里得到输出。