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



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

我想对函数进行单元测试,但要做到这一点,我需要模拟isFile。

我环顾四周,找到了模拟类但不模拟单个函数的方法。

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

您可以为此使用"范围化导入"。请参阅文档中的相应章节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")); 
}

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

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

本地进口Sergei Nosov在某些情况下有效,但我认为顶级的更好,因为通常你会想测试自己的功能:

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";
 }

也就是说,在本地定义fake函数。。。但我也不喜欢,现在我想起来了,因为函数不一定知道它将如何测试。也许导入的mocks模块实际上制作了函数指针或可以在单元测试块中重新分配的东西。。。。嗯,它可能需要一个完整的库,而不仅仅是函数的集合。

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


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

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函数时把它扔出去。

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

您还可以通过编写自己的stdlib模块,赋予它相同的名称,并显式地将其传递给编译器来替换整个stdlib模块。我有时会在火卫一上做开发工作时这样做。但由于这取代了整个,并且是编译器命令行的区别,因此它可能对单元测试也没有帮助。

最新更新