用C语言对Gtk+应用进行功能测试

简介:

用C语言对Gtk+应用进行功能测试

这个简单教程教你如何测试你应用的功能。

自动化测试用来保证你程序的质量以及让它以预想的运行。单元测试只是检测你算法的某一部分,而并不注重各组件间的适应性。这就是为什么会有功能测试,它有时也称为集成测试。

功能测试简单地与你的用户界面进行交互,无论它是网站还是桌面应用。为了展示功能测试如何工作,我们以测试一个 Gtk+ 应用为例。为了简单起见,这个教程里,我们使用 Gtk+ 2.0 教程的示例。

基础设置

对于每一个功能测试,你通常需要定义一些全局变量,比如 “用户交互时延” 或者 “失败的超时时间”(也就是说,如果在指定的时间内一个事件没有发生,程序就要中断)。

 
 
  1. #define TTT_FUNCTIONAL_TEST_UTIL_IDLE_CONDITION(f) ((TttFunctionalTestUtilIdleCondition)(f)) 
  2. #define TTT_FUNCTIONAL_TEST_UTIL_REACTION_TIME (125000) 
  3. #define TTT_FUNCTIONAL_TEST_UTIL_REACTION_TIME_LONG (500000) 
  4. typedef gboolean (*TttFunctionalTestUtilIdleCondition)(gpointer data); 
  5. struct timespec ttt_functional_test_util_default_timeout = { 
  6.   20, 
  7.   0, 
  8. }; 

现在我们可以实现我们自己的超时函数。这里,为了能够得到期望的延迟,我们采用 usleep 函数。

 
 
  1. void 
  2. ttt_functional_test_util_reaction_time() 
  3.   usleep(TTT_FUNCTIONAL_TEST_UTIL_REACTION_TIME); 
  4. void 
  5. ttt_functional_test_util_reaction_time_long() 
  6.   usleep(TTT_FUNCTIONAL_TEST_UTIL_REACTION_TIME_LONG); 

直到获得控制状态,超时函数才会推迟执行。这对于一个异步执行的动作很有帮助,这也是为什么采用这么长的时延。

 
 
  1. void 
  2. ttt_functional_test_util_idle_condition_and_timeout( 
  3.      TttFunctionalTestUtilIdleCondition idle_condition, 
  4.      struct timespec *timeout, 
  5.      pointer data) 
  6.   struct timespec start_time, current_time
  7.   clock_gettime(CLOCK_MONOTONIC, 
  8.                 &start_time); 
  9.   while(TTT_FUNCTIONAL_TEST_UTIL_IDLE_CONDITION(idle_condition)(data)){ 
  10.     ttt_functional_test_util_reaction_time(); 
  11.     clock_gettime(CLOCK_MONOTONIC, 
  12.                   &current_time); 
  13.     if(start_time.tv_sec + timeout->tv_sec < current_time.tv_sec){ 
  14.       break; 
  15.     } 
  16.   } 
  17.   ttt_functional_test_util_reaction_time(); 

与图形化用户界面交互

为了模拟用户交互的操作, Gdk 库 为我们提供了一些需要的函数。要完成我们的工作,我们只需要如下 3 个函数:

  • gdk_display_warp_pointer()
  • gdk_test_simulate_button()
  • gdk_test_simulate_key()

举个例子,为了测试按钮点击,我们可以这么做:

 
 
  1. gboolean 
  2. ttt_functional_test_util_button_click(GtkButton *button) 
  3.   GtkWidget *widget; 
  4.   GdkWindow *window; 
  5.   gint x, y; 
  6.   gint origin_x, origin_y; 
  7.   if(button == NULL || 
  8.      !GTK_IS_BUTTON(button)){ 
  9.     return(FALSE); 
  10.   } 
  11.   widget = button; 
  12.   if(!GTK_WIDGET_REALIZED(widget)){ 
  13.     ttt_functional_test_util_reaction_time_long(); 
  14.   } 
  15.   /* retrieve window and pointer position */ 
  16.   gdk_threads_enter(); 
  17.   window = gtk_widget_get_window(widget); 
  18.   x = widget->allocation.x + widget->allocation.width / 2.0; 
  19.   y = widget->allocation.y + widget->allocation.height / 2.0; 
  20.   gdk_window_get_origin(window, &origin_x, &origin_y); 
  21.   gdk_display_warp_pointer(gtk_widget_get_display(widget), 
  22.                            gtk_widget_get_screen(widget), 
  23.                            origin_x + x, origin_y + y); 
  24.   gdk_threads_leave(); 
  25.   /* click the button */ 
  26.   ttt_functional_test_util_reaction_time(); 
  27.   gdk_test_simulate_button(window, 
  28.                            x, 
  29.                            y, 
  30.                            1, 
  31.                            GDK_BUTTON1_MASK, 
  32.                            GDK_BUTTON_PRESS); 
  33.   ttt_functional_test_util_reaction_time(); 
  34.   gdk_test_simulate_button(window, 
  35.                            x, 
  36.                            y, 
  37.                            1, 
  38.                            GDK_BUTTON1_MASK, 
  39.                            GDK_BUTTON_RELEASE); 
  40.   ttt_functional_test_util_reaction_time(); 
  41.   ttt_functional_test_util_reaction_time_long(); 
  42.   return(TRUE); 

我们想要保证按钮处于激活状态,因此我们提供一个空闲条件函数:

 
 
  1. gboolean 
  2. ttt_functional_test_util_idle_test_toggle_active( 
  3.      GtkToggleButton **toggle_button) 
  4.   gboolean do_idle; 
  5.   do_idle = TRUE
  6.   gdk_threads_enter(); 
  7.   if(*toggle_button != NULL && 
  8.      GTK_IS_TOGGLE_BUTTON(*toggle_button) && 
  9.      gtk_toggle_button_get_active(*toggle_button)){ 
  10.     do_idle = FALSE
  11.   } 
  12.   gdk_threads_leave(); 
  13.   return(do_idle); 

测试场景

因为这个 Tictactoe 程序非常简单,我们只需要确保点击了一个 GtkToggleButton 按钮即可。一旦该按钮肯定进入了激活状态,功能测试就可以执行。为了点击按钮,我们使用上面提到的很方便的 util 函数。

如图所示,我们假设,填满第一行,玩家 A 就赢,因为玩家 B 没有注意,只填充了第二行。

 
 
  1. GtkWindow *window; 
  2. Tictactoe *ttt; 
  3. void* 
  4. ttt_functional_test_gtk_main(void *) 
  5.   gtk_main(); 
  6.   pthread_exit(NULL); 
  7. void 
  8. ttt_functional_test_dumb_player_b() 
  9.   GtkButton *buttons[3][3]; 
  10.   guint i; 
  11.   /* to avoid race-conditions copy the buttons */ 
  12.   gdk_threads_enter(); 
  13.   memcpy(buttons, ttt->buttons, 9 * sizeof(GtkButton *)); 
  14.   gdk_threads_leave(); 
  15.   /* TEST 1 - the dumb player B */ 
  16.   for(i = 0; i < 3; i++){ 
  17.     /* assert player A clicks the button successfully */ 
  18.     if(!ttt_functional_test_util_button_click(buttons[0][i])){ 
  19.       exit(-1); 
  20.     } 
  21.     functional_test_util_idle_condition_and_timeout( 
  22.          ttt_functional_test_util_idle_test_toggle_active, 
  23.          ttt_functional_test_util_default_timeout, 
  24.          &buttons[0][i]); 
  25.     /* assert player B clicks the button successfully */ 
  26.     if(!ttt_functional_test_util_button_click(buttons[1][i])){ 
  27.       exit(-1); 
  28.     } 
  29.     functional_test_util_idle_condition_and_timeout( 
  30.          ttt_functional_test_util_idle_test_toggle_active, 
  31.          ttt_functional_test_util_default_timeout, 
  32.          &buttons[1][i]); 
  33.   } 
  34. int 
  35. main(int argc, char **argv) 
  36.   pthread_t thread; 
  37.   gtk_init(&argc, &argv); 
  38.   /* start the tictactoe application */ 
  39.   window = gtk_window_new(GTK_WINDOW_TOPLEVEL); 
  40.   ttt = tictactoe_new(); 
  41.   gtk_container_add(window, ttt); 
  42.   gtk_widget_show_all(window); 
  43.   /* start the Gtk+ dispatcher */ 
  44.   pthread_create(&thread, NULL
  45.                  ttt_functional_test_gtk_main, NULL); 
  46.   /* launch test routines */ 
  47.   ttt_functional_test_dumb_player_b(); 
  48.   /* terminate the application */ 
  49.   gdk_threads_enter(); 
  50.   gtk_main_quit(); 
  51.   gdk_threads_leave(); 
  52.   return(0); 


原文发布时间为:2017-10-12 

本文作者:Joël Krähemann

本文来自云栖社区合作伙伴“51CTO”,了解相关信息可以关注。

相关文章
|
17天前
|
存储 算法 程序员
C语言:基础与应用的双重魅力
C语言:基础与应用的双重魅力
|
2月前
|
C语言
在C语言中数组作为函数参数的应用与示例
在C语言中数组作为函数参数的应用与示例
17 0
|
2月前
|
算法 C语言
在C语言中函数的递归调用及应用示例
在C语言中函数的递归调用及应用示例
16 1
|
2月前
|
C语言
在C语言中多维数组名作为函数参数的应用与示例
在C语言中多维数组名作为函数参数的应用与示例
14 0
|
2月前
|
存储 程序员 C语言
深入探讨C语言中的字符型数据类型及其应用
深入探讨C语言中的字符型数据类型及其应用
15 0
|
6天前
|
机器学习/深度学习 算法 数据挖掘
【C 言专栏】C 语言与机器学习的应用
【5月更文挑战第6天】C语言在机器学习中扮演关键角色,以其高效性、灵活性和可移植性实现底层算法、嵌入式系统和高性能计算。在神经网络、决策树和聚类算法等领域的实现中不可或缺。C语言被用于TensorFlow和OpenCV等知名库的底层,常与C++、Python结合使用。尽管面临开发难度和适应新算法的挑战,但C语言在机器学习领域的价值和潜力将持续展现,为科技进步贡献力量。
【C 言专栏】C 语言与机器学习的应用
|
8天前
|
存储 缓存 算法
【C 言专栏】C 语言中的数据结构应用
【5月更文挑战第4天】本文探讨了C语言中的核心数据结构,包括数组、链表(单链表和双链表)、栈、队列、二叉树(如二叉搜索树和二叉堆)以及图结构。这些数据结构在程序设计中扮演着关键角色,如数组的快速访问、链表的动态管理、栈和队列的处理流程控制、树和图的复杂关系表示。理解并选择适当的数据结构可优化程序性能,而内存管理和算法优化则进一步提升效率。通过案例分析和展望未来发展趋势,本文旨在帮助读者深化对C语言数据结构的理解和应用。
【C 言专栏】C 语言中的数据结构应用
|
11天前
|
机器学习/深度学习 算法 C语言
【C言专栏】递归算法在 C 语言中的应用
【4月更文挑战第30天】本文介绍了递归算法在C语言中的应用,包括基本概念(通过调用自身解决子问题)、特点(调用自身、终止条件、栈空间)和实现步骤(定义递归函数、分解问题、设置终止条件、组合解)。文中通过阶乘计算和斐波那契数列两个案例展示了递归的使用,强调了递归可能导致的栈溢出问题及优化需求。学习递归有助于理解和应用“分而治之”策略。
|
11天前
|
存储 算法 程序员
【C言专栏】C 语言结构体的应用与实践
【4月更文挑战第30天】C语言中的结构体是自定义数据类型的关键,它组合不同类型的數據以创建新类型,尤其适合处理复杂对象如学生信息。通过定义结构体如`struct Student`,包含名字、学号和成绩,可以方便地实例化和访问成员。结构体在链表实现、函数参数传递和数组中都有广泛应用,如表示链表节点和处理批量数据。理解并熟练运用结构体对于C语言编程至关重要,能提升代码效率和可读性。
|
16天前
|
存储 算法 程序员
C语言:深入探索与实战应用
C语言:深入探索与实战应用
13 0