lunarmodules / busted

Elegant Lua unit testing.
https://lunarmodules.github.io/busted/
MIT License
1.39k stars 184 forks source link

[DOC] Emulating stubs with multiple function calls ? #670

Closed Kochise closed 2 years ago

Kochise commented 2 years ago

Hi, using busted to mock some public functions. Yet those public functions are using local/private functions. Those public functions are calling other modules and/or the std functions.

local module = require("module")

local function private()
    if module.run() then
        return 1
    end
    if module.run() then
        return 2
    end
    return 0
end

function public()
    if private() == 0 then
        ...
    end
end

Now to test stress the public functions, I cannot stub the local/private functions. Stubbing the other modules and/or the std functions is a breeze, somewhat. The main problem is that some of those functions are called multiple times.

    if module.run() then
        return 1
    end
    if module.run() then
        return 2
    end

If I stub them, I can only return one "expected" result at a time. But to test the public functions I need to make the call tree fail->succeed one at a time. Hence modules and/or the std functions should return another result with each call.

module.run.[1].on_call_with(match._).returns(true)
module.run.[2].on_call_with(match._).returns(false)

Is there a way to emulate this behavior using busted ? Like creating a table that will be used to answer each "line" after another for each call. So that I could provide a table that fails at first, then push valid results to test try every code path.

Regards.

Tieske commented 2 years ago

not necessarily Busted, but you could use Lua's dynamic nature.

When testing internals I typically use a global variable to indicate we're testing and export addition functions.

if _TEST then
    local original = private -- store original private value

    function _set_private(func)   -- export as public
        private = func
    end
    function _unset_private(func)   -- export as public
        private = original
    end
end

in the test do _G._TEST = true before loading the module, and then use the before/after handlers to set/unset mock functions

Kochise commented 2 years ago

You mean in the test code ?

I cannot touch the original code.

Tieske commented 2 years ago

No, those flags would work from the source code. So if you cannot touch the source, then I guess you're out of luck.

Kochise commented 2 years ago

Damn, I feel so relieved...

Again.

Btw, thank for your insightful help.

I appreciate that.

I really do.

f197e49ca5d09eb4ad536ba58bdf04ac075b096d