]> granicus.if.org Git - esp-idf/blob - docs/zh_CN/api-guides/unit-tests.rst
doc/unit-tests:translate the unit-tests into zh_CN
[esp-idf] / docs / zh_CN / api-guides / unit-tests.rst
1 ESP32 中的单元测试
2 ==================
3
4 ESP-IDF
5 中附带了一个基于 ``Unity`` 的单元测试应用程序框架,且所有的单元测试用例分别保存在
6 ESP-IDF 仓库中每个组件的 ``test`` 子目录中。
7
8 添加常规测试用例
9 ----------------
10
11 单元测试被添加在相应组件的 ``test`` 子目录中,测试用例写在 C 文件中,一个
12 C 文件可以包含多个测试用例。测试文件的名字要以 “test” 开头。
13
14 测试文件需要包含 ``unity.h`` 头文件,此外还需要包含待测试 C
15 模块需要的头文件。
16
17 测试用例需要通过 C 文件中特定的函数来添加,如下所示:
18
19 .. code:: c
20
21    TEST_CASE("test name", "[module name]"
22    {
23            // 在这里添加测试用例
24    }
25
26 -  第一个参数是字符串,用来描述当前测试。
27
28 -  第二个参数是字符串,用方括号中的标识符来表示,标识符用来对相关测试或具有特定属性的测试进行分组。
29
30 没有必要在每个测试用例中使用 ``UNITY_BEGIN()`` 和 ``UNITY_END()``
31 来声明主函数的区域, ``unity_platform.c`` 会自动调用
32 ``UNITY_BEGIN()``\ , 然后运行测试用例,最后调用 ``UNITY_END()``\ 。
33
34 每一个测试子目录下都应该包含一个
35 ``component.mk``\ ,并且里面至少要包含如下的一行内容:
36
37 .. code:: makefile
38
39    COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive
40
41 更多关于如何在 Unity 下编写测试用例的信息,请查阅
42 http://www.throwtheswitch.org/unity 。
43
44
45 添加多设备测试用例
46 ------------------
47
48 常规测试用例会在一个 DUT(Device Under
49 Test,在试设备)上执行,那些需要互相通信的组件(比如
50 GPIO,SPI...)不能使用常规测试用例进行测试。多设备测试用例支持使用多个
51 DUT 进行写入和运行测试。
52
53 以下是一个多设备测试用例:
54
55 .. code:: c
56
57    void gpio_master_test()
58    {
59        gpio_config_t slave_config = {
60                .pin_bit_mask = 1 << MASTER_GPIO_PIN,
61                .mode = GPIO_MODE_INPUT,
62        };
63        gpio_config(&slave_config);
64        unity_wait_for_signal("output high level");
65        TEST_ASSERT(gpio_get_level(MASTER_GPIO_PIN) == 1);
66    }
67
68    void gpio_slave_test()
69    {
70        gpio_config_t master_config = {
71                .pin_bit_mask = 1 << SLAVE_GPIO_PIN,
72                .mode = GPIO_MODE_OUTPUT,
73        };
74        gpio_config(&master_config);
75        gpio_set_level(SLAVE_GPIO_PIN, 1);
76        unity_send_signal("output high level");
77    }
78
79    TEST_CASE_MULTIPLE_DEVICES("gpio multiple devices test example", "[driver]", gpio_master_test, gpio_slave_test);
80
81 宏 ``TEST_CASE_MULTIPLE_DEVICES`` 用来声明多设备测试用例,
82
83 -  第一个参数指定测试用例的名字。
84
85 -  第二个参数是测试用例的描述。
86
87 -  从第三个参数开始,可以指定最多5个测试函数,每个函数都是单独运行在一个
88    DUT 上的测试入口点。
89
90 在不同的 DUT 上运行的测试用例,通常会要求它们之间进行同步。我们提供
91 ``unity_wait_for_signal`` 和 ``unity_send_signal`` 这两个函数来使用 UART
92 去支持同步操作。如上例中的场景,slave 应该在在 master 设置好 GPIO
93 电平后再去读取 GPIO 电平,DUT 的 UART
94 终端会打印提示信息,并要求用户进行交互。
95
96 DUT1(master)终端:
97
98 .. code:: bash
99
100    Waiting for signal: [output high level]!
101    Please press "Enter" key once any board send this signal.
102
103 DUT2(slave)终端:
104
105 .. code:: bash
106
107    Send signal: [output high level]!
108
109 一旦 DUT2 发送了该信号,您需要在 DUT2 的终端输入回车,然后 DUT1 会从
110 ``unity_wait_for_signal`` 函数中解除阻塞,并开始更改 GPIO 的电平。
111
112
113 添加多阶段测试用例
114 ------------------
115
116 常规的测试用例无需重启就会结束(或者仅需要检查是否发生了重启),可有些时候我们想在某些特定类型的重启事件后运行指定的测试代码,例如,我们想在深度睡眠唤醒后检查复位的原因是否正确。首先我们需要出发深度睡眠复位事件,然后检查复位的原因。为了实现这一点,我们可以定义多阶段测试用例来将这些测试函数组合在一起。
117
118 .. code:: c
119
120    static void trigger_deepsleep(void)
121    {
122        esp_sleep_enable_timer_wakeup(2000);
123        esp_deep_sleep_start();
124    }
125
126    void check_deepsleep_reset_reason()
127    {
128        RESET_REASON reason = rtc_get_reset_reason(0);
129        TEST_ASSERT(reason == DEEPSLEEP_RESET);
130    }
131
132    TEST_CASE_MULTIPLE_STAGES("reset reason check for deepsleep", "[esp32]", trigger_deepsleep, check_deepsleep_reset_reason);
133
134 多阶段测试用例向用户呈现了一组测试函数,它需要用户进行交互(选择用例并选择不同的阶段)来运行。
135
136
137 编译单元测试程序
138 ----------------
139
140 按照 esp-idf 顶层目录的 README 文件中的说明进行操作,请确保 ``IDF_PATH``
141 环境变量已经被设置指向了 esp-idf 的顶层目录。
142
143 切换到 ``tools/unit-test-app`` 目录下进行配置和编译:
144
145 -  ``make menuconfig`` - 配置单元测试程序。
146
147 -  ``make TESTS_ALL=1`` - 编译单元测试程序,测试每个组件 ``test``
148    子目录下的用例。
149
150 -  ``make TEST_COMPONENTS='xxx'`` - 编译单元测试程序,测试指定的组件。
151
152 -  ``make TESTS_ALL=1 TEST_EXCLUDE_COMPONENTS='xxx'`` -
153    编译单元测试程序,测试所有(除开指定)的组件。例如
154    ``make TESTS_ALL=1 TEST_EXCLUDE_COMPONENTS='ulp mbedtls'`` -
155    编译所有的单元测试,不包括 ``ulp`` 和 ``mbedtls``\ 组件。
156
157 当编译完成时,它会打印出烧写芯片的指令。您只需要运行 ``make flash``
158 即可烧写所有编译输出的文件。
159
160 您还可以运行 ``make flash TESTS_ALL=1`` 或者
161 ``make TEST_COMPONENTS='xxx'``
162 来编译并烧写,所有需要的文件都会在烧写之前自动重新编译。
163
164 使用 ``menuconfig`` 可以设置烧写测试程序所使用的串口。
165
166
167 运行单元测试
168 ------------
169
170 烧写完成后重启 ESP32, 它将启动单元测试程序。
171
172 当单元测试应用程序空闲时,输入回车键,它会打印出测试菜单,其中包含所有的测试项目。
173
174 .. code:: bash
175
176    Here's the test menu, pick your combo:
177    (1)     "esp_ota_begin() verifies arguments" [ota]
178    (2)     "esp_ota_get_next_update_partition logic" [ota]
179    (3)     "Verify bootloader image in flash" [bootloader_support]
180    (4)     "Verify unit test app image" [bootloader_support]
181    (5)     "can use new and delete" [cxx]
182    (6)     "can call virtual functions" [cxx]
183    (7)     "can use static initializers for non-POD types" [cxx]
184    (8)     "can use std::vector" [cxx]
185    (9)     "static initialization guards work as expected" [cxx]
186    (10)    "global initializers run in the correct order" [cxx]
187    (11)    "before scheduler has started, static initializers work correctly" [cxx]
188    (12)    "adc2 work with wifi" [adc]
189    (13)    "gpio master/slave test example" [ignore][misc][test_env=UT_T2_1][multi_device]
190            (1)     "gpio_master_test"
191            (2)     "gpio_slave_test"
192    (14)    "SPI Master clockdiv calculation routines" [spi]
193    (15)    "SPI Master test" [spi][ignore]
194    (16)    "SPI Master test, interaction of multiple devs" [spi][ignore]
195    (17)    "SPI Master no response when switch from host1 (HSPI) to host2 (VSPI)" [spi]
196    (18)    "SPI Master DMA test, TX and RX in different regions" [spi]
197    (19)    "SPI Master DMA test: length, start, not aligned" [spi]
198    (20)    "reset reason check for deepsleep" [esp32][test_env=UT_T2_1][multi_stage]
199            (1)     "trigger_deepsleep"
200            (2)     "check_deepsleep_reset_reason"
201
202 常规测试用例会打印用例名字和描述,主从测试用例还会打印子菜单(已注册的测试函数的名字)。
203
204 可以输入以下任意一项来运行测试用例:
205
206 -  引号中的测试用例的名字,运行单个测试用例。
207
208 -  测试用例的序号,运行单个测试用例。
209
210 -  方括号中的模块名字,运行指定模块所有的测试用例。
211
212 -  星号,运行所有测试用例。
213
214 ``[multi_device]`` 和 ``[multi_stage]``
215 标签告诉测试运行者该用例是多设备测试还是多阶段测试。这些标签由
216 ``TEST_CASE_MULTIPLE_STAGES`` 和 ``TEST_CASE_MULTIPLE_DEVICES``
217 宏自动生成。
218
219 一旦选择了多设备测试用例,它会打印一个子菜单:
220
221 .. code:: bash
222
223    Running gpio master/slave test example...
224    gpio master/slave test example
225            (1)     "gpio_master_test"
226            (2)     "gpio_slave_test"
227
228 您需要输入数字以选择在 DUT 上运行的测试。
229
230 与多设备测试用例相似,多阶段测试用例也会打印子菜单:
231
232 .. code:: bash
233
234    Running reset reason check for deepsleep...
235    reset reason check for deepsleep
236            (1)     "trigger_deepsleep"
237            (2)     "check_deepsleep_reset_reason"
238
239 第一次执行此用例时,输入 ``1`` 来运行第一阶段(触发深度睡眠)。在重启
240 DUT 并再次选择运行此用例后,输入 ``2``
241 来运行第二阶段。只有在最后一个阶段通过并且之前所有的阶段都成功触发了复位的情况下,该测试才算通过。