4

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

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

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

4

2 回答 2

4

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

您可以为此目的使用“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")); 
}
于 2014-10-15T19:50:17.657 回答
4

我的简单解决方案是在一个单独的模块中模拟函数,然后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 上进行开发工作时这样做。但是由于这取代了整个事情并且是编译器命令行的差异,它可能对单元测试也没有帮助。

于 2014-10-16T03:32:01.690 回答