为了账号安全,请及时绑定邮箱和手机立即绑定

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

标签:
C

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


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


功能测试简单地与你的用户界面进行交互,无论它是网站还是桌面应用。为了展示功能测试如何工作,我们以测试一个 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);

}

void

ttt_functional_test_util_reaction_time_long()

{

  usleep(TTT_FUNCTIONAL_TEST_UTIL_REACTION_TIME_LONG);

}

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


void

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,

                &start_time);

  while(TTT_FUNCTIONAL_TEST_UTIL_IDLE_CONDITION(idle_condition)(data)){

    ttt_functional_test_util_reaction_time();

    clock_gettime(CLOCK_MONOTONIC,

                  &current_time);

    if(start_time.tv_sec + timeout->tv_sec < 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->allocation.x + widget->allocation.width / 2.0;

  y = widget->allocation.y + widget->allocation.height / 2.0;

  gdk_window_get_origin(window, &origin_x, &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 */

  ttt_functional_test_util_reaction_time();

  gdk_test_simulate_button(window,

                           x,

                           y,

                           1,

                           GDK_BUTTON1_MASK,

                           GDK_BUTTON_PRESS);

  ttt_functional_test_util_reaction_time();

  gdk_test_simulate_button(window,

                           x,

                           y,

                           1,

                           GDK_BUTTON1_MASK,

                           GDK_BUTTON_RELEASE);

  ttt_functional_test_util_reaction_time();

  ttt_functional_test_util_reaction_time_long();

  return(TRUE);

}

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


gboolean

ttt_functional_test_util_idle_test_toggle_active(

     GtkToggleButton **toggle_button)

{

  gboolean do_idle;

  do_idle = TRUE;

  gdk_threads_enter();

  if(*toggle_button != NULL &&

     GTK_IS_TOGGLE_BUTTON(*toggle_button) &&

     gtk_toggle_button_get_active(*toggle_button)){

    do_idle = FALSE;

  }

  gdk_threads_leave();

  return(do_idle);

}

测试场景

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


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


GtkWindow *window;

Tictactoe *ttt;

void*

ttt_functional_test_gtk_main(void *)

{

  gtk_main();

  pthread_exit(NULL);

}

void

ttt_functional_test_dumb_player_b()

{

  GtkButton *buttons[3][3];

  guint i;

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

  gdk_threads_enter();

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

  gdk_threads_leave();

  /* TEST 1 - the dumb player B */

  for(i = 0; i < 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,

         &buttons[0][i]);

    /* assert player B clicks the button successfully */

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

      exit(-1);

    }

    functional_test_util_idle_condition_and_timeout(

         ttt_functional_test_util_idle_test_toggle_active,

         ttt_functional_test_util_default_timeout,

         &buttons[1][i]);

  }

}

int

main(int argc, char **argv)

{

  pthread_t thread;

  gtk_init(&argc, &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(&thread, NULL,

                 ttt_functional_test_gtk_main, NULL);

  /* launch test routines */

  ttt_functional_test_dumb_player_b();

  /* terminate the application */

  gdk_threads_enter();

  gtk_main_quit();

  gdk_threads_leave();

  return(0);

}

(题图:opensource.com)

编译自:https://opensource.com/article/17/7/functional-testing作者: Joël Krähemann
原创:LCTT https://linux.cn/article-8951-1.html译者: sugarfillet

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消