测试框架
测试框架
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
参数。