Delphi 更换功能单元

Delphi 更换功能单元,delphi,unit-testing,Delphi,Unit Testing,我正在为大型Delphi代码库编写一个单元测试基础结构。我想将对SysUtils.FileExists中纯函数的调用(例如)链接到“MockSysUtils.FileExists” 编译器不喜欢使用相同的接口创建SysUtils单元 我想的是在运行时挂接我的模拟函数。现在可能吗 还有其他建议吗 问候, 彼得在运行时替换函数很困难,但通常在技术上是可行的。“所有”您需要做的是: 以相关函数的地址为例 反汇编前5个字节左右(检查RET指令-非常小的例程可能与另一个例程相邻,阻止您替换它) 将其页面

我正在为大型Delphi代码库编写一个单元测试基础结构。我想将对SysUtils.FileExists中纯函数的调用(例如)链接到“MockSysUtils.FileExists”

编译器不喜欢使用相同的接口创建SysUtils单元

我想的是在运行时挂接我的模拟函数。现在可能吗

还有其他建议吗

问候,


彼得

在运行时替换函数很困难,但通常在技术上是可行的。“所有”您需要做的是:

  • 以相关函数的地址为例
  • 反汇编前5个字节左右(检查RET指令-非常小的例程可能与另一个例程相邻,阻止您替换它)
  • 将其页面保护(使用)更改为可写
  • 使用JMP rel32指令(即E9)重写前5个字节
  • 按照正常方式实现版本函数,确保其参数和调用约定与模拟的函数相同
一种更简单的方法是链接到不同版本的SysUtils.pas。这将要求您重新编译RTL和VCL中依赖于SysUtils.pas的所有单元,但这可能比上面描述的函数配置方法要简单得多

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

谢谢

是的,如果有一个TSysUtils类,而不是我可以用MockSysUtils继承它,那就太好了。但事实并非如此,代码库庞大。它将一点一点地被取代,但我想知道是否有一个快速启动的解决方案

对于一个函数来说,第一种方法是可以的,但我想在这种情况下不行

我会选择第二种方法。

你可以用它。使用
HookCode
函数,为其指定要替换的函数的地址和要调用的函数的地址。它将返回一个函数指针,您可以使用该指针调用原始函数并在以后取消挂钩。本质上,它实现了巴里描述的中间三个步骤

我认为MadCodeHook是免费供个人使用的。如果你在寻找比这更自由的东西,你可以试着找到。它使用相同的挂钩技术将Unicode支持注入到一些VCL代码中。你需要一个旧版本,因为最近的版本不再免费。在TntSystem.pas中找到
OverwriteProcedure
函数,您也可以在这里找到如何使用该函数的示例


代码挂接很好,因为它不需要重新编译RTL和VCL,也不需要通过条件编译来控制作用域中的函数。您可以钩住单元测试设置过程中的代码,而原始代码永远不会知道其中的区别。它会认为它在调用原始的
FileExists
函数(因为它是),但当它到达那里时,它会立即跳转到您的模拟版本。

这有点离谱,但这里有另一种选择

在构建单元测试和与之配套的主代码库时,可以grep所有要替换的函数,并指定要使用的单元

而不是

fileexists(MyFilename);
您可以grep fileexists并替换为

MockTests.fileexists(MyFileName);

如果您在构建时这样做(使用自动构建工具),那么它可以很容易地完成,并为您提供最大的灵活性。您可以简单地拥有一个列出所有要替换的函数的配置文件。

您也可以将一个只包含要模拟的函数的单元添加到测试单元的uses子句中。Delphi将始终使用最后列出的单元中的函数。不幸的是,这需要您更改要测试的单元

您的模拟系统单元:

unit MockSysutils;

interface

function FileExists(...) ...
...
end.
您要测试的单元:

unit UnitTotest;

interface

uses
  Sysutils,
  MockSysUtils;

...

  if FileExists(...) then

FileExists现在将从MockSysutils而不是Sysutils调用版本。

“或者您修改uses声明以有条件地引用不同的单元。”-我觉得这一定是最简单的方法。确保您的特殊单元是最后一个,然后您可以重新定义任何函数。它假定单元源可用。如果是的话,那就不用动脑筋了;然而,提问者确实特别询问了运行时!