如何在没有OO的情况下对Lua模块进行单元测试?

时间:2018-05-30 16:03:37

标签: dependency-injection lua functional-programming

在Lua中我可以编写一个简单的模块

local database = require 'database'
local M = {}

function M:GetData()
    return database:GetData()
end

return M

如果需要,将加载一次,所有未来版本将加载相同的副本。

如果我想采用面向对象的方法,我可以做类似的事情:

local M = {}
M.__index = M

function M:GetData() 
    return self.database:GetData()
end

return function(database)
    local newM = setmetatable({}, M)
    newM.database = database
    return newM
end

其中M只加载一次,newM的每个副本只保存自己的数据并使用原始M的方法。

说到测试,使用面向对象的方法,我可以传入一个假的“数据库”版本并检查它是否被调用,但是第一种方法我不能。

所以我的问题是如何让第一种方法支持DI /测试,而不是像类那样?

我的想法是将它包装在这样的封闭中:

local mClosure = function(database)
    local M = {}

    function M:GetData()
        return database:GetData()
    end

    return M
end

return mClosure

但是每次调用它都会创建一个M的新副本,因此它将失去以前两种方法的好处。

1 个答案:

答案 0 :(得分:1)

这显然是Lua debug library的用例。有了它,您只需修改函数的upvalues并注入依赖项。另外,请考虑您可以使用require;只需要一次数据库模块,创建收集数据的小表,然后重定向到原始模块并将其放入package.loaded,以便下次require时,require调用返回修改后的模块版本。面向对象的方法就是如何用Ruby这样的语言来做这种事情,但是在Lua中,我们有更好的方法来获取模块或函数,而不是专门为此目的设计的。

local real_db = require 'db'
local fake_db = setmetatable({}, {__index=db})
fake_db.exec(query) print('running query: '..query) end -- dummy function
fake_db.something(...) print('doing something'); real_db.something(...) end
package.loaded.db = fake_db
require 'my_tests' -- this in turn requires 'db', but gets the fake one
package.loaded.db = real_db
-- After this point, `require 'db'` will return the original module
相关问题