English 中文(简体)
替换功能单位
原标题:
  • 时间:2009-01-22 14:08:46
  •  标签:

我正在为一个大型Delphi代码库编写单元测试基础架构。我想将对SysUtils.FileExists中纯净函数的调用链接到"MockSysUtils.FileExists"。

创建具有相同接口的SysUtils单元不受编译器赞赏。

我所想的是在运行时钩入我的模拟函数。这现在可能吗?

还有其他建议吗? (Hái yǒu qítā jiànyì ma?)

问候,

彼得

问题回答

运行时替换函数是困难的,但通常在技术上是可能的。你只需要做的是:

  • take the address of the function in question
  • disassemble the first 5 bytes or so (to check for a RET instruction - very small routines may abut another routine, preventing you from replacing it)
  • change its page protection (with VirtualProtect) to be writable
  • rewrite the first 5 bytes with a JMP rel32 instruction (i.e. E9 <offset-to-your-func>)
  • implement your version function as normal, making sure it has the same arguments and calling convention as the function you are mocking

一种更简单的方法是链接不同版本的SysUtils.pas。这将需要重新编译所有依赖于SysUtils.pas的RTL和VCL单元,但这可能比以上所述的函数插装方法容易得多。

最简单的方法是语言级别的方法,其中您要么根本不直接依赖于SysUtils(因此可以在更高级别上切换),要么修改uses声明以有条件地引用不同的单元。

你可以使用MadCodeHook来实现这一点。使用HookCode函数, 提供想要替换的函数地址和想要替换的函数地址,它将返回一个函数指针,你可以使用它调用原始函数并在取消挂钩之后使用。本质上,它实现了Barry所描述的中间三个步骤。

我认为对于个人使用,MadCodeHook是免费的。如果你需要更自由的东西,可以尝试寻找Tnt Unicode控件的旧版本。它使用了相同的挂钩技术来将Unicode支持注入到VCL代码中的某些部分。你需要一个旧版本,因为更近期的版本已经不再免费了。在TntSystem.pas 中找到 OverwriteProcedure 函数,这里也有如何使用它的示例。

代码挂钩非常棒,因为它不需要重新编译 RTL 和 VCL,并且它不涉及条件编译来控制哪些函数处于作用域内。你可以从你的单元测试设置过程钩取代码,而原始代码永远不会知道差异。它会认为它正在调用原始的 FileExists 函数(因为它确实是),但当它到达那里时,它会立即跳转到你的模拟版本。

你也可以将仅包含你想要模拟的函数的单元添加到测试单元的 uses 子句中。Delphi 将始终使用列在最后的单元中的函数。不幸的是,这需要你改变想要测试的单元。

你的模拟-Sysutils单元:

unit MockSysutils;

interface

function FileExists(...) ...
...
end.

你想要测试的单位:

unit UnitTotest;

interface

uses
  Sysutils,
  MockSysUtils;

...

  if FileExists(...) then

FileExists现在将调用MockSysutils的版本,而非Sysutils的版本。

谢谢。

是的,例如拥有TSysUtils类作为继承的MockSysUtils类会非常好。但是,现在情况并非如此,代码基础庞大。代码将逐步被替换,但我想知道是否有一个快速解决方案。

第一种方法也许适用于一个函数,但我认为在这种情况下不太适用。

我会选择第二种方法。

这有点偏离主题但这里还有另一个选项。

当构建您的单元测试和主要代码库时,您可以全局搜索您希望替换的所有函数,并指定要使用的单元。

而不是

fileexists(MyFilename);

您可以使用grep命令查找fileexists并进行替换。

MockTests.fileexists(MyFileName);

如果您在构建时使用自动化构建工具完成此操作,它可以轻松完成,并为您提供最大的灵活性。您可以简单地拥有一个配置文件,列出所有要替换的函数。





相关问题
热门标签