Search code examples
unit-testinglualua-busted

Testing code immediately invoked in a script


I'm currently writing unit tests for a lua mod library using Busted. The file in question defines a module with some functions, then calls one of those functions at the bottom to initialize itself.

The problem I'm finding is that Busted appears to be evaluating the required-in file twice.

Test

it('does a thing', function()
    -- Some setup, replacing globals etc
    require('items')
    assert.are_equal(2, #Items._registry)
end)

Module

Items = { _registry = {} }
function Items.do_some_stuff() end
function some_util_func() end
function load_registry()
  print(debug.traceback())
  for i, itm in pairs(Items.do_some_stuff()) do
    Items._registry[i] = itm
  end
end

load_registry()

As you can see, although I've simplified the code and names, the structure is nothing out of the blue (as I understand.)

The test will always fail, because #Items._registry is always 0 (and dumping to the console verifies that). I tried printing inside the method and found it printed twice; then I tried using debug.traceback at the top of that unction and found the below. As you can see, the stack traceback is printing twice, suggesting the code is being evaluated twice.

Is this something anybody else has come across? Am I structuring my test wrong for this scenario? Or this a bug?


stack traceback:
    items.lua:96: in function 'load_registry'
    items.lua:109: in main chunk
    [C]: in function 'require'
    spec/items_pec.lua:50: in function <spec/items_spec.lua:39>
    [C]: in function 'xpcall'
    /usr/local/share/lua/5.2/busted/core.lua:178: in function 'safe'
    /usr/local/share/lua/5.2/busted/init.lua:40: in function 'executor'
    /usr/local/share/lua/5.2/busted/core.lua:312: in function </usr/local/share/lua/5.2/busted/core.lua:312>
    [C]: in function 'xpcall'
    /usr/local/share/lua/5.2/busted/core.lua:178: in function 'safe'
    ...
    /usr/local/share/lua/5.2/busted/core.lua:312: in function 'execute'
    /usr/local/share/lua/5.2/busted/block.lua:155: in function 'execute'
    /usr/local/share/lua/5.2/busted/init.lua:7: in function 'executor'
    /usr/local/share/lua/5.2/busted/core.lua:312: in function </usr/local/share/lua/5.2/busted/core.lua:312>
    [C]: in function 'xpcall'
    /usr/local/share/lua/5.2/busted/core.lua:178: in function 'safe'
    /usr/local/share/lua/5.2/busted/core.lua:312: in function 'execute'
    /usr/local/share/lua/5.2/busted/execute.lua:58: in function 'execute'
    /usr/local/share/lua/5.2/busted/runner.lua:174: in function </usr/local/share/lua/5.2/busted/runner.lua:11>
    /usr/local/lib/luarocks/rocks/busted/2.0.rc12-1/bin/busted:3: in main chunk
    [C]: in ?
stack traceback:
    items.lua:96: in function 'load_registry'
    items.lua:109: in main chunk
    [C]: in function 'require'
    spec/items_spec.lua:15: in main chunk
    [C]: in function 'xpcall'
    /usr/local/share/lua/5.2/busted/core.lua:178: in function 'safe'
    /usr/local/share/lua/5.2/busted/block.lua:146: in function 'execute'
    /usr/local/share/lua/5.2/busted/init.lua:7: in function 'executor'
    /usr/local/share/lua/5.2/busted/core.lua:312: in function </usr/local/share/lua/5.2/busted/core.lua:312>
    [C]: in function 'xpcall'
    /usr/local/share/lua/5.2/busted/core.lua:178: in function 'safe'
    /usr/local/share/lua/5.2/busted/core.lua:312: in function 'execute'
    /usr/local/share/lua/5.2/busted/execute.lua:58: in function 'execute'
    /usr/local/share/lua/5.2/busted/runner.lua:174: in function </usr/local/share/lua/5.2/busted/runner.lua:11>
    /usr/local/lib/luarocks/rocks/busted/2.0.rc12-1/bin/busted:3: in main chunk
    [C]: in ?

Solution

  • The answer to this question lay in some detail I thought was extraneous but wasn't (see my comment).

    In particular, I separated tests for the modules on-load behaviour from the tests for its various functions. Even when running with busted -t to target a specific test, the imports of the module under test were being evaluated in both specs; even when the require call was placed in the setup function for the root describe block.

    By merging the two specs I was able to work around this double-loading.