lunarmodules / busted

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

How to mock Global external library like memcached & mysql in Lua unit testing #626

Closed mbhalodiPlex closed 4 years ago

mbhalodiPlex commented 4 years ago

Hello All,

I am a bit new to Lua 5.3, Vernemq & Busted Framework.

I need some guidance related to Lua Unit Testing using Busted.

I am using the Memcached & MySql library of vernemq in Lua script.

I am stuck at writing unit testing on how to mock those libraries.

I am getting below error while loading the library using require. Without executing a single unit test. attempt to index a nil value (global 'memcached')

Can somebody give me a concrete example?

Thanks

ildar commented 4 years ago

Did you study https://olivinelabs.com/busted/#spies-mocks-stubs ?

mbhalodiPlex commented 4 years ago

Yes, I studied but I couldn't get it entirely. I haven't got any concrete examples on the internet for the same.

I have created several demos but while at a time of loading the library it is giving error (without executing describe block). so I tried a insulate block also before loading the library into the test case Lua file.

Tieske commented 4 years ago

please add a minimal code example that demonstrates your issue.

mbhalodiPlex commented 4 years ago

I have 3 files.

1) cache.lua (Has external library dependency like memcached or mysql) 2) auth.lua (Has dependency on cache.lua) 3) auth-test.lua (For Unit test case, tried different example but when it loading auth.lua that time it gives attempt to index a nil value (global 'memcached') error)

Folder structure

-> lua-scripts -> plugins -> auth.lua -> lua-scripts -> plugins -> cache.lua -> lua-scripts -> Unittests -> auth-test.lua

### cache.lua ->

local cache_module = {} local pool_id = "cache-pool"

memcached.ensure_pool({pool_id = pool_id}) memcached.flush_all(pool_id)

local function get_client_cache(client_id) local client_cache = { // Get cache logic memcached.get(pool_id, client_id, ....) }

return client_cache end

cache_module.get_client_cache = get_client_cache return cache_module

### auth.lua ->

local auth_module = {} local cache = require "plugins/cache" local auth_reg = require "plugins/plex-auth-reg" ...

local function on_register(reg) local client = cache.get_client_cache(reg.client_id)

if (env.is_public()) then auth_reg.auth_register_public_client(reg) else auth_reg.auth_register_internal_client(reg)

if (utils.is_empty(client)) then log.warning("Client (" .. client_id .. ") is not connected.") // Other Logic end

return client end

auth_module.on_register = on_register return auth_module

### auth-test.lua ->

local auth = require "../../plugins/auth" local auth_reg = require "../../plugins/plex-auth-reg"

insulate('mock ensure_pool flush_all', function() local auth local cache = require('../../plugins/cache') package.loaded['../../plugins/cache'] = {}

-- Other functions from 'pl.path' should not be mocked --for k,v in pairs(Cache) do -- package.loaded['../../plugins/cache'][k] = v --end

package.loaded['../../plugins/cache']['ensure_pool'] = function() return true end package.loaded['../../plugins/cache']['flush_all'] = function() return true end package.loaded['../../plugins/cache']['get_client_cache'] = function() return { true } end

-- Reload auth lib so it uses the mocked 'cache' package.loaded['../../plugins/auth'] = nil subc = require('../../plugins/auth')

describe("plugins/auth.lua", function()
    describe("on_register", function()
        it("should register the client", function()

            local reg = {}
            reg.addr = "192.168.123.123" --IP Address e.g.
            reg.port = 12345 -- Port e.g.
            reg.mountpoint = "" -- Mountpoint e.g.
            reg.username = "test-user" --UserName e.g.
            reg.password = "test-password" -- Password e.g.
            reg.client_id = "test-id" -- ClientId e.g.
            reg.clean_session = true --CleanSession Flag

            -- stub(auth_reg, "auth_register_public_client")
            -- stub(auth_reg, "auth_register_internal_client")
            -- auth_reg.auth_register_public_client(reg)
            -- assert.stub(auth_reg.auth_register_public_client).was.called_with(reg)
            -- assert.stub(auth_reg.auth_register_internal_client).was.called_with(reg)

            local result = auth.on_register(reg)

            --assert.stub(auth.auth_register_public_client).was.called_with(reg)
            --assert.stub(auth.auth_register_internal_client).was.called_with(reg)
            assert.False(result)
        end)
    end)
end)

-- Reset mock package.loaded['../../plugins/cache'] = nil package.loaded['subc'] = nil require('subc') end)

mbhalodiPlex commented 4 years ago

I did it using _G variable and expose block.

You just need to make sure below code runs before running test cases. so you need to run all test cases in sorting manner & rename the below file to start with A letter.

Thank you.

expose("an exposed memcached", function() _G.memcached = {} _G.memcached.ensure_pool = function (pool) print(pool) end _G.memcached.flush_all = function (poolId) print(poolId) end _G.memcached.get = function (pool_id, client_id) return false end

end)