【问题标题】:What's the easiest way to access other .exe data in delphi?在delphi中访问其他.exe数据的最简单方法是什么?
【发布时间】:2013-02-04 20:00:06
【问题描述】:

我尝试使用 Delphi 2010 附带的 DUnit 对一个 1000 万个 LOC 项目实施一些基本的自动化测试,这些项目不遵循良好的 OO 实践(例如:将业务逻辑隔离到类/单元中)。我不能对该项目进行正常的单元测试,因为业务逻辑的每个部分都分布在数十个相互依赖的单元中,但是这些单元“组”以某些“主要业务逻辑屏幕”为中心(例如:所有与发票逻辑相关的单元都是以主发票屏幕为中心),并且由于这些屏幕是类,因此我可以进行“主要业务逻辑屏幕类测试”而不是单元测试,但是这些“主屏幕”仍然需要在流程启动期间创建的大量内容。

所以我需要两者:

  • 能够运行不良项目的启动内​​容
  • 能够访问其对象

糟糕的项目已经有一些导出的函数,它们返回指针,我可以强制转换以访问它的对象,但我无法调用它们:

如果我将坏项目创建为测试进程的子进程,启动代码运行良好,但如果没有复杂的 IPC 方法或对坏项目的结构进行实质性更改,我找不到调用导出函数的方法。

如果我像 LoadLibrary 函数一样将坏项目的 .exe 作为 dll 加载,调用坏项目导出的任何函数都会导致访问冲突和/或段错误,即使是这个简单的过程:

procedure Test; {safecall;} {stdcall;}
begin
  showmessage('Yay!');
end;

我怎样才能做到这两点?

【问题讨论】:

  • 如果你不能编写代码在被测进程中运行,那么你需要IPC。

标签: delphi ipc automated-tests dunit


【解决方案1】:

您正在谈论的方法(使用导出的函数)不会成功。两个 Win32 程序之间最简单的通信形式是让它们使用 SendMessage 或 PostMessage 相互通信。定位窗口句柄(通常通过窗口类名)是第 1 步,发送消息是第 2 步。

其次,DUnit 离目标还很远,而且 TTestCase 不能巧妙地扩展为 GUI 控制器,因为这不是它的用途。它用于单元测试。圆钉,方孔。为您可以配置和测试的类编写 TTestCases,并使用 DUnit 为您可以为其提供测试覆盖的系统部分提供测试覆盖。

对于 UI 测试,请使用完全独立的框架。 Delphi 程序员为您提议的那种自动化集成测试采用了两种基本方法。

  1. 自定义 hack 作业。这就是你所描述的。在 Embarcadero 内部,有一个名为 Zombie 的框架,这是 Nick 在 2007 年的博客中提到的。它的方法基于几种“原始 IPC”,通常涉及从程序外部到一个 Win32 SendMessage 或 PostMessage 窗口消息到一个程序内部控件的窗口句柄。然而,内部代码被显着修改以允许僵尸测试。不,您不能拥有 Teh Codez,它们是 Embarcadero 内部和专有的。但它确实说明该方法确实有效,并且不需要重写整个应用程序或编写大量模拟类,如单元测试 会做。如果您想走 hack 路线,您将编写自己的用户界面测试框架,它可能应该与 DUnit 代码完全分离并且不使用 DUnit 代码。欢迎您尝试,但我告诉您,这是严重的阻抗不匹配。如果我在 2013 年开始自己的自定义框架,它将基于 DCOM,因为 Delphi DCOM 服务器代码可以简单地有条件地编译成许多程序,并且 DCOM 会为您处理函数调用“编组”细节。我怀疑我会在该项目中投入一年,然后我会放弃,因为最后,我怀疑我能否使任何系统(基于 DCOM 或 Win32 消息)得到回报。

  2. 一个完整的外部测试工具,您可以在其中编写测试脚本,例如 AutomatedQA/SmartBear TestComplete。您的测试不会被编译成 delphi 测试程序,而是在 TestComplete 中运行,类似 Pascal 的脚本语法只是编写测试脚本的可用选项之一。

【讨论】:

  • 当我说我想测试屏幕时,我指的不是 UI,而是屏幕(表单)的类,因为每个屏幕都有自己的类。另一件事是我不需要将坏项目用作进程,我只需要能够访问它的导出函数并同时运行它的启动内容。
  • 我理解你的愿望,但我想说你的愿望是混乱和有缺陷的,而且不会奏效。如果你想测试类,你的工具是单元测试、模拟、接口、依赖注入等等,而不是 DLL hacks。
【解决方案2】:

我们在这里遇到了同样的问题。看起来您确实需要一个 delphi 库项目 (*.dll) 才能使导出函数工作,(我怀疑直接在可执行文件上调用函数时不会初始化框架,没有保证)。

注意:我们仍在使用 Delphi 5,所以这里没有 dunit 集成。

我们使用的解决方案是使用条件 DEFINE 将 dunit 源添加到我们的项目(.exe 项目)中,并在启动单元中使用此条件定义。

我们应用程序的示例启动代码:

  if ComServer.StartMode <> smAutomation then
  begin
    OurApplication.Login ;
  end;
{$IFDEF _AS_TESTRUNNER_}
    GUITestRunner.RunRegisteredTests;
{$ELSE}

  if OurApplication.HasStartCommands then
  begin
    Application.ShowMainForm := False ;
  end
  else begin
    if ComServer.StartMode = smAutomation then
      Application.ShowMainForm := False
  end;

  Application.Run;
{$ENDIF}

  OurApplication.Finalize;

当我使用 _AS_TESTRUNNER_ 条件定义时,我必须先登录,以便我们的应用程序(和数据库连接)被初始化。跟随 bij DUnit 的 GUITestrunner。

可以在初始化部分注册测试用例,就像在示例中一样。

像魅力一样工作。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-03-28
    • 1970-01-01
    • 2016-10-23
    • 2015-06-08
    • 2014-01-27
    • 1970-01-01
    • 2011-06-20
    • 2010-09-22
    相关资源
    最近更新 更多