对 Python GUI 应用程序进行单元测试的推荐方法是什么?

发布于 2024-12-01 17:37:05 字数 1226 浏览 3 评论 0原文

我目前很愚蠢地尝试为 Python 桌面应用程序维护两个并行代码库,一个使用 GTK 3 的 PyGObject 自省,另一个使用 GTK 2 的 PyGTK。我主要在 PyGObject 分支上工作,然后将更改移植到PyGTK 分支。由于这些实现之间的所有细微差别,我经常忽略一些事情并导致我错过并意外释放的破坏,结果却被用户发现。

我正在尝试找出一种设计一些单元测试的好方法,这些单元测试最好适合在两个代码库上运行。它不是一个过于复杂的程序,(它本质上是一个库管理工具,想象一下像 iTunes):

- Main Window
  |- Toolbar with some buttons (add/edit/remove items, configure the program)
  |
  |- VPaned
  |--- Top HPaned
  |------ ListView (listing values by which a library of items can be filtered)
  |------ ListView (listing the contents of the library
  |--- Bottom HPaned
  |------ Image (displaying cover art for the currently selected item in the library)
  |------ TextView (displaying formatted text describing the currently selected item)
 - Edit dialog
 - Configuration dialog
 - About dialog 

我尝试尽可能地将视图与模型分开。其中每一项都在其自己的类中实现(嗯,是在从列出的 GTK 类继承的类中)。 ListViews 与继承自ListStores 的其他类结合在一起。库本身由不同的类处理。尽管如此,小部件之间仍存在需要测试的交互。例如,如果用户在过滤器视图中选择特定项目,过滤库,然后从过滤结果中选择一个项目,则文本视图必须显示正确的库条目的信息,由于翻译,这是半复杂的在 TreeModelFilter 和原始 ListStore 之间进行迭代,等等。

那么,我问,为这样的 GUI 应用程序编写健壮的单元测试的推荐方法是什么?我已经看到有一些用于此目的的库,但 pygtk 的主要库已经多年没有更新,因此它们几乎肯定会因 PyGObject 内省而失败。也许我没有足够的创造力来找出使用 Python 的 unittest 模块来实现这一点的好方法,所以我愿意接受建议。

I'm currently foolish enough to try to maintaintain two parallel code bases for a Python desktop application, one using PyGObject introspection for GTK 3 and one using PyGTK for GTK 2. I work mainly on the PyGObject branch and then I port changes over to the PyGTK branch. Due to all the minor differences between these implementations, I often overlook things and cause breakage that I miss and accidentally release, only to be caught by the users.

I'm trying to figure out a good way to design some unittests that would, preferably, be suitable to run on both code bases. It's not an overly complicated program, (it's essentially a library management tool, imagine like iTunes):

- Main Window
  |- Toolbar with some buttons (add/edit/remove items, configure the program)
  |
  |- VPaned
  |--- Top HPaned
  |------ ListView (listing values by which a library of items can be filtered)
  |------ ListView (listing the contents of the library
  |--- Bottom HPaned
  |------ Image (displaying cover art for the currently selected item in the library)
  |------ TextView (displaying formatted text describing the currently selected item)
 - Edit dialog
 - Configuration dialog
 - About dialog 

I've tried to separate views from models as much as possible. Each of those items is implemented in its own class (well, in classes which inherit from the GTK classes listed). The ListViews are coupled with other classes which inherit from ListStores. The library itself is handled by a different class. Nonetheless, there are interactions between the widgets that need to be tested. For example, if the user selects a particular item in the filter view, filtering the library, and then selects an item from the filtered results, the text view must display the information for the correct library entry, which is semi-complicated due to translating iters between TreeModelFilter and the original ListStore, etc etc.

So, I ask, what is the recommended way for writing robust unit tests for such a GUI application? I've seen that there are some libraries for this but the main ones for pygtk haven't been updated in years and so they will almost certainly fail with PyGObject introspection. Perhaps I'm not creative enough to figure out a good way to do it using Python's unittest module, so I'm open to suggestions.

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

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

发布评论

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

评论(4

水水月牙 2024-12-08 17:37:05

您确定要对 GUI进行单元测试吗?您的复杂性示例涉及超过 1 个单元,因此是一个集成测试。

如果您确实想要进行单元测试,您应该能够实例化一个类,为其依赖项提供模拟或存根,然后像 GUI 框架那样调用它的方法,例如用户单击。这可能很乏味,并且您必须确切地知道 GUI 框架如何将用户输入分派到您的类。

我的建议是在模型中放入更多的东西。对于给定的示例,您可以创建一个 FilterManager,它抽象单个方法背后的所有过滤/选择/显示内容。然后进行单元测试。

Are you sure you want to unit test the GUI? Your example of complexity involves more than 1 unit and is therefore an integration test.

If you really want to unit test, you should be able to instantiate a single class providing mocks or stubs for its dependencies, then call methods on it like the GUI framework would for e.g. a user click. This can be tedious and you have to know exactly how the GUI framework dispatches user input to your classes.

My advice is to put even more stuff in models. For your given example you could create a FilterManager which abstracts all the filter/select/display stuff behind a single method. Then unit test it.

但可醉心 2024-12-08 17:37:05

有一种很好的方法可以直接测试 PyGTK 功能和小部件,而无需通过验收/功能/集成测试框架,这些框架可能会被遗忘。我在这篇文章中了解了这一点,这是相当不言自明的。但基本思想是,您将小部件视为函数/类,并且可以直接测试它们。如果您需要处理回调等,我将在这里重现一个巧妙的技巧:

import time
import gtk

# Stolen from Kiwi
def refresh_gui(delay=0):
  while gtk.events_pending():
      gtk.main_iteration_do(block=False)
  time.sleep(delay)

正如博客文章中提到的,此代码是 LGPL。否则,当您考虑一下时,只要您不 show() 窗口或小部件,您就可以测试它们,并且它们应该表现得像真实的一样,因为,在某种程度上,他们是。它们只是不显示。

当然,您需要自己模拟按钮和交互式小部件的交互,例如在按钮上调用 clicked() 。再次参见 Ali Afshar 关于 PyGTK 单元测试的优秀文章

There is a great way to test PyGTK functions and widgets directly, without going through acceptance/functional/integration testing frameworks that clikety their way into oblivion. I learned about this in this post which is fairly self explanatory. But the basic idea is that you treat your widgets as functions/classes, and you can test them directly. If you need to process callbacks and so on, there's a neat trick that I will reproduce here:

import time
import gtk

# Stolen from Kiwi
def refresh_gui(delay=0):
  while gtk.events_pending():
      gtk.main_iteration_do(block=False)
  time.sleep(delay)

As mentionned in the blog post, this code is LGPL. Otherwise, when you think about it, as long as you don't show() windows or widgets, you can test them all you want and they should behave as if they were real because, in a way, they are. They are just not displayed.

Of course, you need to simulate interaction on buttons and interactive widgets yourself by calling clicked() on a button for example. See again Ali Afshar's excellent post about unit testing in PyGTK.

月棠 2024-12-08 17:37:05

坚持 Jürgen 正确的主题,因为我对单元测试不感兴趣,但实际上对集成测试感兴趣,我还从 freedesktop.org 找到了这个框架:
http://ldtp.freedesktop.org/wiki/

它允许自动执行各种辅助功能测试 -启用 GUI 应用程序(包括 GTK、Qt、Swing 等)。

In sticking with the theme that Jürgen was correct in that I'm not interested in unit testing, but actually interested in integration testing, I also found this framework from freedesktop.org:
http://ldtp.freedesktop.org/wiki/

It allows automating a variety of tests for Accessibility-enabled GUI applications (including GTK, Qt, Swing, etc).

我喜欢麦丽素 2024-12-08 17:37:05

您可以使用 X11 帧缓冲区:

Xvfb :119 -screen 0 1024x768x16 &
export DISPLAY=:119
... run your tests

请确保不要输入 gtk.main(),因为这会等待鼠标或键盘输入。您可以使用此模式让 gtk 处理所有事件:

def refresh_gui():
  while gtk.events_pending():
      gtk.main_iteration_do(block=False)

据我所知,您看不到您的应用程序,但您可以测试您的回调。

You can use a X11 framebuffer:

Xvfb :119 -screen 0 1024x768x16 &
export DISPLAY=:119
... run your tests

Be sure, to not enter gtk.main(), since this would wait for mouse or keyboard input. You can use this pattern to let gtk handle all events:

def refresh_gui():
  while gtk.events_pending():
      gtk.main_iteration_do(block=False)

AFAIK you can't see your application, but you can test your callbacks.

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