如何在D中模拟标准库函数

时间:2014-10-14 22:17:15

标签: function unit-testing mocking d phobos

我有一个函数,它在文件名上调用isFile(来自std.file),然后继续附加.1,.2,.3等,检查每个文件是否存在。

我想对该函数进行单元测试,但为此我需要模拟isFile。

我环顾四周,找到了模拟课程但不是单一功能的方法。

2 个答案:

答案 0 :(得分:3)

由于我的答案与亚当的答案略有不同,我会添加它,他可以添加他的答案。

您可以使用“Scoped imports”来实现此目的。请参阅文档http://dlang.org/module.html

中的相应部分

这里还有一个工作示例,如何在单元测试块中模拟isFile函数(假设它在模块“mocks”中定义)

import std.file; 
import std.stdio;

int main(string[] args) 
{ 
    writeln(isFile("qq.d")); 
    return 0; 
} 

unittest 
{ 
    import mocks;
    writeln(isFile("qq.d")); 
}

答案 1 :(得分:3)

我的简单解决方案是在单独的模块中模拟函数,然后使用version(unittest)选择您想要的函数:

version(unittest)
   import mocks.file;
else
   import std.file

void main() { isFile("foo"); } // std.file normally, mocks.file in test mode

当地进口谢尔盖诺索夫在某些情况下有效,但我认为顶级进口更好,因为通常你想测试自己的功能:

string test_me() { isFile("qq.d"); return "do something"; }
unittest {
    assert(test_me() == "do something");
}

在这种情况下,范围导入将无法正常工作,因为isFile的使用距离测试太远。但是,使用点导入的version(unittest)可以根据需要重新定义函数。

也许最好的是组合:

string test_me() {
    version(unittest) bool isFile(string) { return true; }
    else import std.file : isFile;
    isFile("qq.d"); return "do something";
 }

也就是说,在本地定义假功能......但我现在也不喜欢它,因为功能并不一定知道如何测试它。也许导入的mocks模块实际上会生成函数指针或可以在unittest块中重新分配的东西....嗯,它可能需要是一个完整的库,而不仅仅是函数集合。

但我认为在我们的两个答案中,有一个潜在的解决方案。


我想提到的第三件事,虽然它有点疯狂,但是可以通过使用一些链接器技巧来全局替换另一个模块中的函数:

import std.file;
import std.stdio;

// our replacement for isFile...
pragma(mangle, std.file.isFile.mangleof)
static bool isFile(string) { return true; }

int main(string[] args)
{
    writeln(isFile("qq.d")); // always does true
    return 0;
}

有效的原因是pragma(mangle)改变了链接器看到的名称。如果链接器看到两个具有相同名称的函数,一个在库中,一个在用户代码中,则允许用户代码替换单个库函数。

因此,我们的函数被用来代替lib。重要说明:功能签名必须匹配,否则在运行时会崩溃,它会替换整个程序的功能,而不仅仅是一个位置。可以与版本(unittest)一起使用。

我不推荐实际使用这个技巧,如果你犯了一个错误就容易发生随机崩溃,只是想在替换std lib函数的时候把它扔掉。

也许这个技巧加上函数指针可以用来在运行时替换一个函数。但是主要问题是:由于链接器完全用你的函数替换库函数,你实际上根本不能使用原始实现!

您也可以通过编写自己的std lib模块来替换它,使其具有相同的名称,并将其明确地传递给编译器。我有时会在对Phobos进行开发工作时这样做。但由于这取代了整个并且是编译器命令行差异,因此它对单元测试也没有帮助。