我有一个函数,它在文件名上调用 isFile(来自 std.file),然后继续追加 .1、.2、.3 等,检查其中的每一个是否存在。
我想对函数进行单元测试,但为此我需要模拟 isFile。
我环顾四周,找到了模拟类而不是单个函数的方法。
我有一个函数,它在文件名上调用 isFile(来自 std.file),然后继续追加 .1、.2、.3 等,检查其中的每一个是否存在。
我想对函数进行单元测试,但为此我需要模拟 isFile。
我环顾四周,找到了模拟类而不是单个函数的方法。
由于我的回答与亚当的略有不同,所以我会添加它,他可以添加他的。
您可以为此目的使用“Scoped imports”。请参阅文档http://dlang.org/module.html中的相应部分
这也是一个工作示例,如何isFile
在单元测试块中模拟函数(假设它在模块“模拟”中定义)
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";
}
也就是说,在本地定义假函数......但我也不喜欢那样,现在我想到了,因为函数不一定知道如何测试它。也许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。重要说明:函数签名必须匹配,否则运行时它会崩溃,它会替换整个程序的函数,而不仅仅是一个位置。可以与 version(unittest) 一起使用。
我不建议实际使用这个技巧,如果你犯了错误,它很容易发生随机崩溃,只是想在考虑替换标准库函数时把它扔掉。
也许这个技巧加上函数指针可用于在运行时替换函数。但是,主要问题是:由于链接器完全用您的函数替换了库函数,因此您实际上根本无法使用原始实现!
您还可以通过编写自己的标准库模块来替换整个标准库模块,给它起相同的名称,然后将其显式传递给编译器。我有时会在 Phobos 上进行开发工作时这样做。但是由于这取代了整个事情并且是编译器命令行的差异,它可能对单元测试也没有帮助。