测试框架

测试框架

GLib 提供了一个框架,用于编写和维护与待测代码并行的单元测试。该 API 是根据其他测试框架(JUnit、NUnit、RUnit)中的既定概念设计的,而这些概念又基于 Smalltalk 单元测试 概念。

  • 测试用例:将测试(测试方法)与装置结合在一起,形成测试 用例。
  • 装置:测试装置由装置数据以及设置和拆卸方法组成,用于为测试函数建立环境。我们使用全新装置,即在每次测试调用前后,装置都会被重新设置和拆卸,以避免测试之间的依赖性。
  • 测试套件:可以将测试用例分组到测试套件中,以便运行可用测试的子集。测试套件也可以分组到其他测试 套件中。

API 被设计用于隐式处理测试套件和测试用例的创建和注册。一个简单的调用 ,如下所示

g_test_add_func ("/misc/assertions", test_assertions);

创建了一个名为“misc”的测试套件,其中包含一个名为“assertions”的测试用例,该用例包括运行 test_assertions 函数。

除了传统的 g_assert_true() 之外,测试框架还提供了一组扩展的比较断言:g_assert_cmpfloat()g_assert_cmpfloat_with_epsilon()g_assert_cmpint()g_assert_cmpuint()g_assert_cmphex()g_assert_cmpstr()g_assert_cmpmem()g_assert_cmpvariant()。与普通 g_assert_true() 相比,这些变体的优点是断言信息可以更加详细,并包括比较的 实体的值。

请注意,不应该在单元测试中使用 g_assert(),因为在使用 G_DISABLE_ASSERT 编译时,它是一个空操作。在生产代码中使用 g_assert(),在单元 测试中使用 g_assert_true()

使用 装置创建包含两个测试的测试套件的完整示例

#include <glib.h>
#include <locale.h>

typedef struct {
  MyObject *obj;
  OtherObject *helper;
} MyObjectFixture;

static void
my_object_fixture_set_up (MyObjectFixture *fixture,
                          gconstpointer user_data)
{
  fixture->obj = my_object_new ();
  my_object_set_prop1 (fixture->obj, "some-value");
  my_object_do_some_complex_setup (fixture->obj, user_data);

  fixture->helper = other_object_new ();
}

static void
my_object_fixture_tear_down (MyObjectFixture *fixture,
                             gconstpointer user_data)
{
  g_clear_object (&fixture->helper);
  g_clear_object (&fixture->obj);
}

static void
test_my_object_test1 (MyObjectFixture *fixture,
                      gconstpointer user_data)
{
  g_assert_cmpstr (my_object_get_property (fixture->obj), ==, "initial-value");
}

static void
test_my_object_test2 (MyObjectFixture *fixture,
                      gconstpointer user_data)
{
  my_object_do_some_work_using_helper (fixture->obj, fixture->helper);
  g_assert_cmpstr (my_object_get_property (fixture->obj), ==, "updated-value");
}

int
main (int argc, char *argv[])
{
  setlocale (LC_ALL, "");

  g_test_init (&argc, &argv, NULL);

  // Define the tests.
  g_test_add ("/my-object/test1", MyObjectFixture, "some-user-data",
              my_object_fixture_set_up, test_my_object_test1,
              my_object_fixture_tear_down);
  g_test_add ("/my-object/test2", MyObjectFixture, "some-user-data",
              my_object_fixture_set_up, test_my_object_test2,
              my_object_fixture_tear_down);

  return g_test_run ();
}

在项目中集成 GTest

使用 Meson

如果您正在使用 Meson 构建系统,通常将使用提供的 test() 基元调用测试二进制文件, 例如

test(
  'foo',
  executable('foo', 'foo.c', dependencies: deps),
  env: [
    'G_TEST_SRCDIR=@0@'.format(meson.current_source_dir()),
    'G_TEST_BUILDDIR=@0@'.format(meson.current_build_dir()),
  ],
  protocol: 'tap',
)

test(
  'bar',
  executable('bar', 'bar.c', dependencies: deps),
  env: [
    'G_TEST_SRCDIR=@0@'.format(meson.current_source_dir()),
    'G_TEST_BUILDDIR=@0@'.format(meson.current_build_dir()),
  ],
  protocol: 'tap',
)

使用 Autotools

如果您正在使用 Autotools,强烈建议您使用 Automake TAP 线束;GLib 提供了模板文件,以便轻松与之集成 

您可以将这些文件复制到您自己的项目的根目录中,然后设置 Makefile.am 文件以引用它们,例如 

include $(top_srcdir)/glib-tap.mk

# test binaries
test_programs = \
  foo \
  bar

# data distributed in the tarball
dist_test_data = \
  foo.data.txt \
  bar.data.txt

# data not distributed in the tarball
test_data = \
  blah.data.txt

请务必分发 TAP 文件,在顶级 Makefile.am 中使用类似以下的内容

EXTRA_DIST += \
  tap-driver.sh \
  tap-test

由于包含在 Makefile.am 中,因此将隐式分发 glib-tap.mk。应该将所有这三个文件添加到版本 控制。

如果您无法访问 Autotools TAP 线束,您可以使用 gtester 和 gtester-report 工具,并使用 GLib 提供的 glib.mk Automake 模板。但是,请注意,自 GLib 2.62 以来,已经弃用了 gtester 和 gtester-report,取而代之的是使用 TAP。自 GLib 2.62 开始,默认情况下启用测试的 --tap 参数。