DUnit GUI 测试:我可以强制“应用程序”吗?到不同的“形式”?

发布于 2025-01-07 14:25:37 字数 1098 浏览 0 评论 0原文

我正在尝试使用 DUnit 对主窗体为其自身创建动态框架的应用程序运行 GUI 单元测试。我已经能够将要测试的应用程序的主窗体创建为测试用例中的表单并访问其菜单项等。

当应用程序尝试动态创建框架时,问题就出现了。框架的资源读取达到需要窗口句柄的程度(在我的例子中,设置选项卡的标题)。这里它从TWinControl.GetHandle到TWinControl.CreateWnd再到TCustomFrame.CreateParams。

在这个CreateParams中,代码说:

  if Parent = nil then
    Params.WndParent := Application.Handle;

这就是差异发生的地方。当我运行实际应用程序(不在测试中)时,此处的 Application.Handle 返回一个非零数字,并且流程继续正常。但在 DUnit 测试应用程序中,此处的 Application.Handle 返回 0。这会导致 TWinControl.CreateWnd 中的代码引发异常,告知该框架没有父级:

  with Params do
  begin
    if (WndParent = 0) and (Style and WS_CHILD <> 0) then
      if (Owner <> nil) and (csReading in Owner.ComponentState) and
        (Owner is TWinControl) then
        WndParent := TWinControl(Owner).Handle
      else
        raise EInvalidOperation.CreateFmt(SParentRequired, [Name]);

我想尝试解决此问题(一般来说,所有测试问题),而无需仅仅因为测试而修改“生产”代码。您能否提供任何线索,说明我是否可以以某种方式强制“应用程序”执行其他操作,或者以其他方式解决此问题?

查看代码,其他可能的解决方法方案可能是尝试让所有者(这是我要测试的应用程序的“MainForm”,即我想要获取其句柄)在执行时处于 csReading 状态在测试中创建了这个框架,但至少一开始似乎也不是那么容易实现这一点。

I'm trying to run a GUI unit test with DUnit to an application whose mainform creates dynamically frames to itself. I've been able to create the application-to-test's mainform as a form in the test case and access its menu items etc.

The problem comes when the application tries to create a frame dynamically. The frame's resource reading comes to a point where it needs the window handle (in my case, setting the caption of a tab sheet). Here it goes from TWinControl.GetHandle to TWinControl.CreateWnd and to TCustomFrame.CreateParams.

In this CreateParams, the code says:

  if Parent = nil then
    Params.WndParent := Application.Handle;

This is where the difference occurs. When I run the actual application (not in the test), the Application.Handle here returns a non-zero number and the flow continues ok. But in the DUnit test application, the Application.Handle here returns 0. This causes the code in the TWinControl.CreateWnd to raise an exception telling that the frame does not have a parent:

  with Params do
  begin
    if (WndParent = 0) and (Style and WS_CHILD <> 0) then
      if (Owner <> nil) and (csReading in Owner.ComponentState) and
        (Owner is TWinControl) then
        WndParent := TWinControl(Owner).Handle
      else
        raise EInvalidOperation.CreateFmt(SParentRequired, [Name]);

I'd like to try to get around this problem (and in general, all test problems) without modifying the "production" code just because of the tests. Can you provide any clues on whether I could somehow force the "Application" to something else, or in some other way work around this?

Looking at the code, a possible other workaround scenario might be to try to get the owner (which is my "MainForm" of the application-to-test, i.e. whose handle I'd want to get) to be in csReading state while doing this frame creation in the test, but at least initially it doesn't seem so straightforward to get this to happen either.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

帅哥哥的热头脑 2025-01-14 14:25:37

您应该创建一个TForm,并将frame.parent 设置为该TForm,而不是解决设置Application.Handle 的方法。

//Dunit Test Scaffolding code...Set up a workable environment for the test:
aForm := TForm.Create(nil);
aFrame := TFrame.Create(aForm);
aFrame.Parent := aForm;

在真实的应用程序中,框架将有一个父级(通常是窗口、TForm 或 TPanel 的父级)。您试图告诉一个框架在没有父框架的情况下运行,而 TFrame 的设计目的并非如此。

Instead of working around a way to set Application.Handle, you should create a TForm, and set your frame.parent to be that TForm.

//Dunit Test Scaffolding code...Set up a workable environment for the test:
aForm := TForm.Create(nil);
aFrame := TFrame.Create(aForm);
aFrame.Parent := aForm;

In real apps, frames will have a parent (be parented to a window, a TForm or TPanel usually). You are trying to tell a frame to run without a parent, which TFrame is not designed to do.

神仙妹妹 2025-01-14 14:25:37

感谢所有的评论和回复!
我相信已经解决了这些问题,至少是到目前为止发现的问题。
我将在下面总结我的发现和最终情况(以防其他人发现这些有用)。

我有一个继承自 TTestSetup 的测试装饰器类,它包含对其在必要时创建的虚拟(主)表单的引用。

我还找到了一种使用如下方法在运行时切换 Application.MainForm 的方法: http ://www.swissdelphicenter.ch/torry/showcode.php?id=665

在测试装饰器SetUp方法中,我首先创建虚拟表单,然后将其设置为应用程序的主表单(这里可能不需要此设置)。

然后我有一个测试用例类(继承自 TGUITestCase),每个测试都会运行其 SetUp 和 TearDown 。在此设置中,我创建了正在测试的主窗体,然后将其设置为应用程序的主窗体。然后,在测试用例的 TearDown 中进行测试后,我再次将虚拟表单设置为应用程序的主表单,并且仅在调用 Close 并 Free 到我正在测试的主表单之后。否则,释放当前为 Application.MainForm 的窗体将导致整个 DUnit 应用程序终止。

Thanks for all the comments and replies!
I believe to have solved the issues, at least ones found out so far.
I'll summarize my findings and final situation below (in case someone else would find any of this useful).

I have a test decorator class inheriting from TTestSetup, which holds a reference to a dummy (main) form that it creates when necessary.

I also found a way to switch the Application.MainForm on runtime using approach like this: http://www.swissdelphicenter.ch/torry/showcode.php?id=665

In the test decorator SetUp method I create first the dummy form and then set it as the main form of the Application (this setting might not be necessary here).

Then I have a test case class (inheriting from TGUITestCase), whose SetUp and TearDown are run for each test. In this SetUp I create the mainform that I'm testing and then set it as the Application's main form. Then after the test in the test case's TearDown, I set the dummy form again to be the main form of the Application, and only after this call Close and Free to the mainform that I'm testing. Otherwise freeing a form that is currently the Application.MainForm would cause the entire DUnit application to terminate.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文