天天看點

用C語言對Gtk+應用進行功能測試

<a href="http://s3.51cto.com/wyfs02/M01/08/4F/wKiom1nfPTLyee8UAAD0CZRlZGA708.png-wh_651x-s_1657377668.png" target="_blank"></a>

這個簡單教程教你如何測試你應用的功能。

自動化測試用來保證你程式的品質以及讓它以預想的運作。單元測試隻是檢測你算法的某一部分,而并不注重各元件間的适應性。這就是為什麼會有功能測試,它有時也稱為內建測試。

功能測試簡單地與你的使用者界面進行互動,無論它是網站還是桌面應用。為了展示功能測試如何工作,我們以測試一個 Gtk+ 應用為例。為了簡單起見,這個教程裡,我們使用 Gtk+ 2.0 教程的示例。

基礎設定

對于每一個功能測試,你通常需要定義一些全局變量,比如 “使用者互動時延” 或者 “失敗的逾時時間”(也就是說,如果在指定的時間内一個事件沒有發生,程式就要中斷)。

#define TTT_FUNCTIONAL_TEST_UTIL_IDLE_CONDITION(f) ((TttFunctionalTestUtilIdleCondition)(f)) 

#define TTT_FUNCTIONAL_TEST_UTIL_REACTION_TIME (125000) 

#define TTT_FUNCTIONAL_TEST_UTIL_REACTION_TIME_LONG (500000) 

typedef gboolean (*TttFunctionalTestUtilIdleCondition)(gpointer data); 

struct timespec ttt_functional_test_util_default_timeout = { 

  20, 

  0, 

}; 

現在我們可以實作我們自己的逾時函數。這裡,為了能夠得到期望的延遲,我們采用 usleep 函數。

void 

ttt_functional_test_util_reaction_time() 

  usleep(TTT_FUNCTIONAL_TEST_UTIL_REACTION_TIME); 

ttt_functional_test_util_reaction_time_long() 

  usleep(TTT_FUNCTIONAL_TEST_UTIL_REACTION_TIME_LONG); 

直到獲得控制狀态,逾時函數才會推遲執行。這對于一個異步執行的動作很有幫助,這也是為什麼采用這麼長的時延。

ttt_functional_test_util_idle_condition_and_timeout( 

     TttFunctionalTestUtilIdleCondition idle_condition, 

     struct timespec *timeout, 

     pointer data) 

  struct timespec start_time, current_time; 

  clock_gettime(CLOCK_MONOTONIC, 

                &amp;start_time); 

  while(TTT_FUNCTIONAL_TEST_UTIL_IDLE_CONDITION(idle_condition)(data)){ 

    ttt_functional_test_util_reaction_time(); 

    clock_gettime(CLOCK_MONOTONIC, 

                  &amp;current_time); 

    if(start_time.tv_sec + timeout-&gt;tv_sec &lt; current_time.tv_sec){ 

      break; 

    } 

  } 

  ttt_functional_test_util_reaction_time(); 

與圖形化使用者界面互動

為了模拟使用者互動的操作, Gdk 庫 為我們提供了一些需要的函數。要完成我們的工作,我們隻需要如下 3 個函數:

gdk_display_warp_pointer()

gdk_test_simulate_button()

gdk_test_simulate_key()

舉個例子,為了測試按鈕點選,我們可以這麼做:

gboolean 

ttt_functional_test_util_button_click(GtkButton *button) 

  GtkWidget *widget; 

  GdkWindow *window; 

  gint x, y; 

  gint origin_x, origin_y; 

  if(button == NULL || 

     !GTK_IS_BUTTON(button)){ 

    return(FALSE); 

  widget = button; 

  if(!GTK_WIDGET_REALIZED(widget)){ 

    ttt_functional_test_util_reaction_time_long(); 

  /* retrieve window and pointer position */ 

  gdk_threads_enter(); 

  window = gtk_widget_get_window(widget); 

  x = widget-&gt;allocation.x + widget-&gt;allocation.width / 2.0; 

  y = widget-&gt;allocation.y + widget-&gt;allocation.height / 2.0; 

  gdk_window_get_origin(window, &amp;origin_x, &amp;origin_y); 

  gdk_display_warp_pointer(gtk_widget_get_display(widget), 

                           gtk_widget_get_screen(widget), 

                           origin_x + x, origin_y + y); 

  gdk_threads_leave(); 

  /* click the button */ 

  gdk_test_simulate_button(window, 

                           x, 

                           y, 

                           1, 

                           GDK_BUTTON1_MASK, 

                           GDK_BUTTON_PRESS); 

                           GDK_BUTTON_RELEASE); 

  ttt_functional_test_util_reaction_time_long(); 

  return(TRUE); 

我們想要保證按鈕處于激活狀态,是以我們提供一個空閑條件函數:

ttt_functional_test_util_idle_test_toggle_active( 

     GtkToggleButton **toggle_button) 

  gboolean do_idle; 

  do_idle = TRUE; 

  if(*toggle_button != NULL &amp;&amp; 

     GTK_IS_TOGGLE_BUTTON(*toggle_button) &amp;&amp; 

     gtk_toggle_button_get_active(*toggle_button)){ 

    do_idle = FALSE; 

  return(do_idle); 

測試場景

因為這個 Tictactoe 程式非常簡單,我們隻需要確定點選了一個 GtkToggleButton 按鈕即可。一旦該按鈕肯定進入了激活狀态,功能測試就可以執行。為了點選按鈕,我們使用上面提到的很友善的 util 函數。

如圖所示,我們假設,填滿第一行,玩家 A 就赢,因為玩家 B 沒有注意,隻填充了第二行。

GtkWindow *window; 

Tictactoe *ttt; 

void* 

ttt_functional_test_gtk_main(void *) 

  gtk_main(); 

  pthread_exit(NULL); 

ttt_functional_test_dumb_player_b() 

  GtkButton *buttons[3][3]; 

  guint i; 

  /* to avoid race-conditions copy the buttons */ 

  memcpy(buttons, ttt-&gt;buttons, 9 * sizeof(GtkButton *)); 

  /* TEST 1 - the dumb player B */ 

  for(i = 0; i &lt; 3; i++){ 

    /* assert player A clicks the button successfully */ 

    if(!ttt_functional_test_util_button_click(buttons[0][i])){ 

      exit(-1); 

    functional_test_util_idle_condition_and_timeout( 

         ttt_functional_test_util_idle_test_toggle_active, 

         ttt_functional_test_util_default_timeout, 

         &amp;buttons[0][i]); 

    /* assert player B clicks the button successfully */ 

    if(!ttt_functional_test_util_button_click(buttons[1][i])){ 

         &amp;buttons[1][i]); 

int 

main(int argc, char **argv) 

  pthread_t thread; 

  gtk_init(&amp;argc, &amp;argv); 

  /* start the tictactoe application */ 

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL); 

  ttt = tictactoe_new(); 

  gtk_container_add(window, ttt); 

  gtk_widget_show_all(window); 

  /* start the Gtk+ dispatcher */ 

  pthread_create(&amp;thread, NULL, 

                 ttt_functional_test_gtk_main, NULL); 

  /* launch test routines */ 

  ttt_functional_test_dumb_player_b(); 

  /* terminate the application */ 

  gtk_main_quit(); 

  return(0); 

(題圖:opensource.com)

本文作者:Joël Krähemann

來源:51CTO

繼續閱讀