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

Posted

技术标签:

【中文标题】对 Python GUI 应用程序进行单元测试的推荐方法是啥?【英文标题】:What's the recommended way to unittest Python GUI applications?对 Python GUI 应用程序进行单元测试的推荐方法是什么? 【发布时间】:2011-11-04 15:05:24 【问题描述】:

我目前愚蠢到试图为 Python 桌面应用程序维护两个并行代码库,一个使用 PyGObject 自省用于 GTK 3,一个使用 PyGTK 用于 GTK 2。我主要在 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 模块的好方法,所以我愿意接受建议。

【问题讨论】:

在有人问之前:pygtk 不支持 GTK 3,但我发现对 GTK 2 的 pygobject 自省的支持太不完整,无法依赖。 对我来说,在使用 GUI 时避免回归的最佳方法是进行手动测试。 Unittest 非常适合测试工作功能(以 MVC 为例),但我不知道如何处理 gui... 这是我一直在做的,但我显然不是很擅长,因为每次发布我都会错过一些东西。 你签出@unpythonic.blogspot.com/2007/03/unit-testing-pygtk.html了吗? 你应该把它作为答案。我没遇到过这种方法。我想我可以使用这种方法拼凑一些复杂的测试脚本。 【参考方案1】:

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

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

我的建议是在模型中添加更多内容。对于您给定的示例,您可以创建一个过滤器管理器,它将所有过滤器/选择/显示内容抽象在一个方法后面。然后进行单元测试。

【讨论】:

啊,也许我的术语有误(我学习/使用这些已经快 10 年了)。那么,如果我可能天真地问,集成测试是否像单元测试一样具有强大的定义或可设计性?尽管如此,我确实认为其中很多都可以按照我编写的方式进行单元测试,使用对我的问题的评论中提到的方法。 嗯,单元测试的核心概念是一次测试一个单元,以保持测试完整、简单、快速并能够准确地查明故障。有时这很难,尤其是对于 GUI。你或许也可以使用 Python 的一些 unittest 模块进行非单元测试,但是你也失去了一些好处,我不会再称之为单元测试了。 这不是一个真正的答案...显然他想要使用 PyGTK 进行集成测试,所以请指出他正确的方向,而不是更正术语。 @schlamar 我希望您能提供答案,而不是暗示不同的接受和否决。我自己没有集成测试经验。 也许你应该看看这个链接:obeythetestinggoat.com/…【参考方案2】:

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

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's excellent post about unit testing in PyGTK。

【讨论】:

【参考方案3】:

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

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

【讨论】:

【参考方案4】:

您可以使用 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)

AFAIK 你看不到你的应用程序,但你可以测试你的回调。

【讨论】:

xvfb-run -a testname 更好

以上是关于对 Python GUI 应用程序进行单元测试的推荐方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

PyQt.QTableView 内容的单元测试

如何对绘制 PDF 图形的 Python 函数进行单元测试?

我如何对这样的代码进行单元测试?

你如何对 Python DataFrames 进行单元测试

如何对 python 异步函数进行单元测试?

使用python对pyspark代码进行单元测试