4

So I'm using Busted to create unit tests for an existing Lua file, without changing the code in the file if possible. The file imports another file, and then stores various methods from that file in local functions, like so.

[examplefile.lua]
local helper = require "helper.lua"
local helper_accept = helper.accept
local helper_reject = helper.reject

foo = new function()
  -- do something which uses helper_accept
  -- do something which uses helper_reject
end

I want to spy on these methods in my tests to ensure that they have been called at the right places. However, I can't find any way to do this from the test. I've tried simply mocking out the helper methods, as in:

[exampletest.lua]

local helper = require "helper.lua"
local examplefile = require "examplefile.lua"

-- mock the helper function to simply return true
helper.accept = new function() return true end
spy.on(helper, "accept")
examplefile:foo
assert.spy(helper).was().called()

but that doesn't work as the real file uses the helper_accept and helper_reject methods, not helper.accept and helper.reject.

Can this be done without changing the code? Thanks.

Thomas W
  • 14,757
  • 6
  • 48
  • 67
Ryan
  • 115
  • 1
  • 8
  • No. Local files aren't exported, so you can't override them. But you can override the entire helper.lua file, so that when it loads `helper.accept` and `helper.reject`, it uses your hooked version of the functions. – ktb Jun 08 '17 at 23:54
  • @ktb That sounds like the logical way forward. Do you know of any documentation which would help me do this? – Ryan Jun 09 '17 at 14:22

1 Answers1

4

The easiest way I can think of for accomplishing this is to override the "helper" library with hook stubs. You can do this by modifying the package.loaded table. The package.loaded table stores the result of an initial call to require "lib", so that if the same require is called again, the module does not need to be reloaded. If you place something in there before the first call to require "lib", it will never actually load the library from the filesystem.

In your case you may want to actually load the library, but hook all the library accesses. I'd do that something like this...

local lib = require "lib"

local function hook_func(_, key)
    print('Accessing "lib" attribute '..tostring(key))
    -- other stuff you might want to do in the hook
    return lib[key]
end

package.loaded["lib"] = setmetatable({}, {__index = hook_func})
ktb
  • 1,498
  • 10
  • 27