Search code examples
unit-testinglualua-5.1

Resetting package.loaded after a test in Lua


I am developing unit tests in Lua and a major part of these tests is changing package.loaded. This goes from

a = require "parser"
a.b = nil

to

package.loaded["checker"] = function() return true end

And the situation where I modify an entry of package.loaded that was loaded before the test started.

I would like to reset package.loaded after every test. But I have a hard time. I tried wrapping require, which solves some problems, but does not solve the problem of the second and the third example. How can I properly make a savepoint of package.loaded right before the tests and reload that savepoint just before I start a new test? Or just roll back package.loaded to the one just after the interpeter started?


Solution

  • This is actually the answer of @EgorSkriptunoff, but he placed it as a comment.

    For me, it was good enough to just make a shallow-copy of bot the package.loaded and the _G. Shallow-copy can be found here: http://lua-users.org/wiki/CopyTable:

    function shallowcopy(orig)
        local orig_type = type(orig)
        local copy
        if orig_type == 'table' then
            copy = {}
            for orig_key, orig_value in pairs(orig) do
                copy[orig_key] = orig_value
            end
        else -- number, string, boolean, etc
            copy = orig
        end
        return copy
    end
    

    The actual testing then looks like this:

    local packageLoadedCopy = shallowcopy(package.loaded)
    local gCopy = shallowcopy(_G)
    for _, test in ipairs(tests) do
        -- Do the actual test
        -- Clean up the environment
        local markDeletion = {}
        local markModify = {}
        for name in pairs(package.loaded) do
            if not packageLoadedCopy[name] then
                table.insert(markDeletion, name)
            elseif packageLoadedCopy[name] ~= package.loaded[name] then
                table.insert(markModify, name)
            end
        end
        for _, name in pairs(markDeletion) do
            package.loaded[name] = nil
        end
        for _, name in pairs(markModify) do
            package.loaded[name] = packageLoadedCopy[name]
        end
        markDeletion, markModify = {}, {}
        for name in pairs(_G) do
            if not gCopy[name] then
                table.insert(markDeletion, name)
            elseif _G[name] ~= gCopy[name] then
                table.insert(markModify, name)
            end
        end
        for _, name in pairs(markDeletion) do
            _G[name] = nil
        end
        for _, name in pairs(markModify) do
            _G[name] = gCopy[name]
        end
    end
    

    Do note that simply

    package.loaded = packageLoadedCopy
    _G = gCopy
    

    Does not work, and I am not sure why.