测试框架

Ztest 提供了一个简单的测试框架,用于在开发时进行测试。它提供了基本的断言宏以及一个通用的测试结构体。

该框架可以以两种方式进行使用,既可以作为集成测试的通用框架,也可以用作指定模块的单元测试。

快速开始 - 集成测试

一个简单的工作基石位于 samples/testing/integration 。您只需要拷贝文件到 tests/ 中并根据自己的需要进行编辑。sanitycheck 脚本会自动编译并运行该测试。如果您需要测 foobar 组件,您应当将例程文件夹拷贝至 test/foo/bar。然后,您就可以使用 ./scripts/sanitycheck -s tests/foo/bar/test 进行测试。

例程包含下面的文件:

Makefile

1
2
3
4
BOARD ?= qemu_x86
CONF_FILE ?= prj.conf

include $(ZEPHYR_BASE)/Makefile.inc

testcase.ini

1
2
[test]
tags = my_tags

prj.conf

1
CONFIG_ZTEST=y

src/Makefile

1
2
3
obj-y = main.o

include $(ZEPHYR_BASE)/tests/Makefile.test

src/main.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/*
 * Copyright (c) 2016 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <ztest.h>

static void assert_tests(void)
{
	assert_true(1, "1 was false");
	assert_false(0, "0 was true");
	assert_is_null(NULL, "NULL was not NULL");
	assert_not_null("foo", "\"foo\" was NULL");
	assert_equal(1, 1, "1 was not equal to 1");
	assert_equal_ptr(NULL, NULL, "NULL was not equal to NULL");
}

void test_main(void)
{
	ztest_test_suite(framework_tests,
		ztest_unit_test(assert_tests)
	);

	ztest_run_test_suite(framework_tests);
}

快速开始 - 单元测试

Ztest 可以用于单元测试。这意味着您测试某个单一的功能时不必包含整个 Zephyr OS 工程,您只需要专注于某个指定模块的效果。这样会加速测试,因为只有该模块会被编译,且被测试函数会被直接调用。

由于没有包含依赖的基本内核数据结构体,您必须在测试时提供函数桩。Ztest 为 mocking 函数提供了一些有用的帮助,请参考后面的介绍。

在单元测试中,mock 对象可用于仿真真实的复杂对象的行为,还可以通过判断某个对象是否有交互作用而判断测试是否成功。如果有需要,还可以断言交互的顺序。

samples/testing/unit 文件夹中包含了测试 Zephyr 的 net-buf 的 API 的例程。

Makefile

1
2
3
INCLUDE += subsys

include $(ZEPHYR_BASE)/tests/unit/Makefile.unittest

testcase.ini

1
2
3
[test]
type = unit
tags = buf

main.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
/*
 * Copyright (c) 2016 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <ztest.h>

unsigned int irq_lock(void)
{
	return 0;
}

void irq_unlock(unsigned int key)
{
}

#include <net/buf.c>

void k_queue_init(struct k_queue *fifo) {}
void k_queue_append_list(struct k_queue *fifo, void *head, void *tail) {}

int k_is_in_isr(void)
{
	return 0;
}

void *k_queue_get(struct k_queue *fifo, int32_t timeout)
{
	return NULL;
}

void k_queue_append(struct k_queue *fifo, void *data)
{
}

void k_queue_prepend(struct k_queue *fifo, void *data)
{
}

#define TEST_BUF_COUNT 1
#define TEST_BUF_SIZE 74

NET_BUF_POOL_DEFINE(bufs_pool, TEST_BUF_COUNT, TEST_BUF_SIZE,
		    sizeof(int), NULL);

static void test_get_single_buffer(void)
{
	struct net_buf *buf;

	buf = net_buf_alloc(&bufs_pool, K_NO_WAIT);

	assert_equal(buf->ref, 1, "Invalid refcount");
	assert_equal(buf->len, 0, "Invalid length");
	assert_equal(buf->flags, 0, "Invalid flags");
	assert_equal_ptr(buf->frags, NULL, "Frags not NULL");
}

void test_main(void)
{
	ztest_test_suite(net_buf_test,
		ztest_unit_test(test_get_single_buffer)
	);

	ztest_run_test_suite(net_buf_test);
}

API reference

运行测试

警告

doxygengroup: Cannot find file: /home/docs/checkouts/readthedocs.org/user_builds/zephyr-doc/checkouts/latest/doc/doxygen/xml/index.xml

断言

如果相关的断言失败,这些宏将立即失败。当断言失败后,它会打印当前文件、行数和函数,以及失败的原因和可选消息。如果配置选项 CONFIG_ZTEST_ASSERT_VERBOSE=0 ,断言时只会打印文件和行数,从而可以减小测试镜像的尺寸。

Example output for a failed macro from assert_equal(buf->ref, 2, “Invalid refcount”):

Assertion failed at main.c:62: test_get_single_buffer: Invalid refcount (buf->ref not equal to 2)
Aborted at unit test function

警告

doxygengroup: Cannot find file: /home/docs/checkouts/readthedocs.org/user_builds/zephyr-doc/checkouts/latest/doc/doxygen/xml/index.xml

Mocking

这些功能允许对回调函数和相关函数进行抽象化,并在测试中进行控制。您可以修改测试的配置文件中的选项 CONFIG_ZTEST_MOCKING=y 来使能 mocking 框架。并发返回值和期待的参数的数量由 ZTEST_PARAMETER_COUNT 进行限制。

下面是一个例程,它希望函数 expect_two_parametersa=2b=3,然后 returns_int 返回 5

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <ztest.h>

static void expect_two_parameters(int a, int b)
{
	ztest_check_expected_value(a);
	ztest_check_expected_value(b);
}

static void parameter_tests(void)
{
	ztest_expect_value(expect_two_parameters, a, 2);
	ztest_expect_value(expect_two_parameters, b, 3);
	expect_two_parameters(2, 3);
}

static int returns_int(void)
{
	return ztest_get_return_value();
}

static void return_value_tests(void)
{
	ztest_returns_value(returns_int, 5);
	assert_equal(returns_int(), 5, NULL);
}

void test_main(void)
{
	ztest_test_suite(mock_framework_tests,
		ztest_unit_test(parameter_test),
		ztest_unit_test(return_value_test)
	);

	ztest_run_test_suite(mock_framework_tests);
}

警告

doxygengroup: Cannot find file: /home/docs/checkouts/readthedocs.org/user_builds/zephyr-doc/checkouts/latest/doc/doxygen/xml/index.xml