我有一个函数,它对文件名调用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模块。我有时会在火卫一上做开发工作时这样做。但由于这取代了整个,并且是编译器命令行的区别,因此它可能对单元测试也没有帮助。