¿Cuál es la forma recomendada para probar las aplicaciones de la GUI de Python?

Actualmente soy lo suficientemente tonto como para tratar de mantener dos bases de código paralelas para una aplicación de escritorio Python, una que usa la introspección PyGObject para GTK 3 y la otra que usa PyGTK para GTK 2. Trabajo principalmente en la twig PyGObject y luego transfiero los cambios a la Sucursal PyGTK. Debido a todas las pequeñas diferencias entre estas implementaciones, a menudo me olvido de las cosas y causo roturas que extraño y libero accidentalmente, solo para ser atrapado por los usuarios.

Estoy tratando de encontrar una buena manera de diseñar algunas pruebas de unidad que, de preferencia, serían adecuadas para ejecutarse en ambas bases de código. No es un progtwig demasiado complicado (es esencialmente una herramienta de administración de bibliotecas, imagínate como 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 

He intentado separar las vistas de los modelos tanto como sea posible. Cada uno de esos elementos se implementa en su propia clase (bueno, en las clases que heredan de las clases GTK enumeradas). Las ListViews se combinan con otras clases que heredan de ListStores. La biblioteca en sí es manejada por una clase diferente. No obstante, hay interacciones entre los widgets que necesitan ser probados. Por ejemplo, si el usuario selecciona un elemento particular en la vista de filtro, filtra la biblioteca y luego selecciona un elemento de los resultados filtrados, la vista de texto debe mostrar la información para la entrada correcta de la biblioteca, que es semi-complicada debido a la traducción. iters entre TreeModelFilter y el ListStore original, etc. etc.

Entonces, pregunto, ¿cuál es la forma recomendada para escribir pruebas unitarias robustas para una aplicación GUI de este tipo? He visto que hay algunas bibliotecas para esto, pero las principales para pygtk no se han actualizado en años, por lo que es casi seguro que fallarán con la introspección PyGObject. Quizás no soy lo suficientemente creativo como para encontrar una buena manera de hacerlo usando el módulo de unittest de unittest de Python, así que estoy abierto a sugerencias.

¿Estás seguro de que quieres probar la GUI? Su ejemplo de complejidad implica más de 1 unidad y, por lo tanto, es una prueba de integración.

Si realmente desea realizar una prueba unitaria, debería ser capaz de crear una instancia de una sola clase que proporcione simulacros o apéndices para sus dependencias, luego llame a los métodos como el marco de la GUI para, por ejemplo, un clic del usuario. Esto puede ser tedioso y debe saber exactamente cómo el marco de la GUI distribuye las opiniones de los usuarios a sus clases.

Mi consejo es poner aún más cosas en los modelos. Para su ejemplo dado, puede crear un FilterManager que resum todo el filtro / selección / visualización de cosas detrás de un solo método. Entonces la unidad lo prueba.

Existe una excelente manera de probar las funciones y los widgets de PyGTK directamente, sin pasar por marcos de prueba de aceptación / funcional / integración que se abren camino hacia el olvido. Aprendí sobre esto en este post que es bastante auto explicativo. Pero la idea básica es que tratas tus widgets como funciones / clases, y puedes probarlos directamente. Si necesitas procesar devoluciones de llamada y demás, hay un truco genial que reproduciré aquí:

 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) 

Como se menciona en la publicación del blog, este código es LGPL. De lo contrario, cuando lo pienses, siempre y cuando no show() ventanas o widgets, puedes probarlos todo lo que quieras y deberían comportarse como si fueran reales porque, en cierto modo, lo son. Simplemente no se muestran.

Por supuesto, necesita simular la interacción en botones y widgets interactivos usted mismo haciendo clicked() en un botón, por ejemplo. Vea nuevamente la excelente publicación de Ali Afshar sobre pruebas de unidad en PyGTK .

Siguiendo con el tema de que Jürgen tenía razón en que no estoy interesado en las pruebas de unidad , pero en realidad estoy interesado en las pruebas de integración, también encontré este marco en freedesktop.org: http://ldtp.freedesktop.org/wiki/

Permite automatizar una variedad de pruebas para aplicaciones GUI habilitadas para accesibilidad (incluyendo GTK, Qt, Swing, etc.).

Puedes usar un framebuffer X11:

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

Asegúrese de no ingresar gtk.main() , ya que esto esperaría la entrada del mouse o del teclado. Puedes usar este patrón para que gtk maneje todos los eventos:

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

AFAIK no puede ver su aplicación, pero puede probar sus devoluciones de llamada.