我有一个函数,它在文件名上调用isFile(来自std.file),然后继续附加.1,.2,.3等,检查每个文件是否存在。
我想对该函数进行单元测试,但为此我需要模拟isFile。
我环顾四周,找到了模拟课程但不是单一功能的方法。
答案 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进行开发工作时这样做。但由于这取代了整个并且是编译器命令行差异,因此它对单元测试也没有帮助。