Delphi 更换功能单元
我正在为大型Delphi代码库编写一个单元测试基础结构。我想将对SysUtils.FileExists中纯函数的调用(例如)链接到“MockSysUtils.FileExists” 编译器不喜欢使用相同的接口创建SysUtils单元 我想的是在运行时挂接我的模拟函数。现在可能吗 还有其他建议吗 问候,Delphi 更换功能单元,delphi,unit-testing,Delphi,Unit Testing,我正在为大型Delphi代码库编写一个单元测试基础结构。我想将对SysUtils.FileExists中纯函数的调用(例如)链接到“MockSysUtils.FileExists” 编译器不喜欢使用相同的接口创建SysUtils单元 我想的是在运行时挂接我的模拟函数。现在可能吗 还有其他建议吗 问候, 彼得在运行时替换函数很困难,但通常在技术上是可行的。“所有”您需要做的是: 以相关函数的地址为例 反汇编前5个字节左右(检查RET指令-非常小的例程可能与另一个例程相邻,阻止您替换它) 将其页面
彼得在运行时替换函数很困难,但通常在技术上是可行的。“所有”您需要做的是:
- 以相关函数的地址为例
- 反汇编前5个字节左右(检查RET指令-非常小的例程可能与另一个例程相邻,阻止您替换它)
- 将其页面保护(使用)更改为可写
- 使用JMP rel32指令(即E9)重写前5个字节
- 按照正常方式实现版本函数,确保其参数和调用约定与模拟的函数相同
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声明以有条件地引用不同的单元。”-我觉得这一定是最简单的方法。确保您的特殊单元是最后一个,然后您可以重新定义任何函数。它假定单元源可用。如果是的话,那就不用动脑筋了;然而,提问者确实特别询问了运行时!