mmc*_*lk1 4 c c++ embedded unit-testing googletest
我想使用 Google Tests 为我的嵌入式应用程序软件编写单元测试。
这些测试将在用 C++ 编写的应用软件上执行。I2C应用软件(例如, )使用的驱动程序SPI,故障断言是用 C 编写的。我的问题是:
void read(uint8_t address)我的 I2C 库中有一个函数,我该如何模拟该函数,以便在我的 C++ 类中调用该特定函数?Leo*_*rdo 10
我最近使用 gTest ( GoogleTest ) 针对 Arm Cortex-M3 内核测试了 FAT 文件系统和引导加载程序实现,因此我将留下我的两分钱。
嵌入式软件测试存在的问题是无法通过模拟来复制硬件环境。我想出了三组测试:
A)在我的 PC 上运行的单元测试(我在TDD中使用)。我使用这些测试来开发我的应用程序逻辑。这就是我需要嘲笑/存根的地方。我的公司使用硬件抽象层(HAL),这就是我嘲笑的。如果您想编写可测试的代码,最后一点是基础。
/* this is not testable */
my_register->bit0 = 1;
/* this is also not testable */
*my_register |= BIT0;
Run Code Online (Sandbox Code Playgroud)
不要直接访问寄存器,使用可以模拟的简单 HAL 包装函数:
/* this is testable */
void set_bit(uint32_t* reg, uint8_t bit)
{
*reg |= bit;
}
set_bit(my_register , BIT0);
Run Code Online (Sandbox Code Playgroud)
后者是可测试的,因为您将模拟该set_bit函数,从而打破对硬件的依赖。
B) 对目标进行单元测试。这是比 (A) 小得多的测试集,但它仍然很有用,特别是对于测试驱动程序和 HAL 函数。这些测试背后的想法是我可以正确测试我将模拟的函数。因为它在目标上运行,所以我需要尽可能简单和轻量级,所以我使用MinUnit,它是一个 C 头文件。我已经在 Cortex-M3 内核和专有 DSP 代码上使用 MinUnit 运行了目标测试(未经任何修改)。我在这里也使用了 TDD。
C) 集成测试。我在这里使用 Python 和 Behave 在目标上构建、下载和运行整个应用程序。
回答您的问题:
正如其他人已经说过的,从gTest Primer开始,不要担心嘲笑,只需掌握使用 gTest 的窍门即可。提供一些内存检查(泄漏)的一个很好的替代方案是Cpputest。我对派生安装类的 gTest 语法有一点偏好。Cpputest 可以运行用 gTest 编写的测试。两者都是很棒的框架。
我使用Fake Function Frakework进行模拟和存根。它使用起来非常简单,并且提供了一个好的模拟框架所期望的一切:设置不同的返回值、传递回调、检查参数调用历史记录等。我想尝试一下Ceedling。到目前为止,FFF 一直很棒。
我不这样做。我使用 C++ 编译器(在我的例子中为 g++)编译测试框架和测试,并使用 C 编译器 (gcc) 编译嵌入式代码,然后将它们链接在一起。从下面的示例中,您将看到我没有在 C 文件中包含 C++ 头文件。链接测试时,您将链接除您正在模拟的函数的 C 源文件之外的所有内容。
使用代码管理失败的断言 - 我的驱动程序库中的失败断言,要求系统重置。我如何在测试中模拟这一点?
我会模拟重置功能,添加回调来“重置”您需要的任何内容。
假设您要测试read_temperature使用该read函数的函数。下面是一个使用 FFF 进行模拟的 gTest 示例。
hal_i2c.h
/* this is not testable */
my_register->bit0 = 1;
/* this is also not testable */
*my_register |= BIT0;
Run Code Online (Sandbox Code Playgroud)
read_temperature.h
/* this is testable */
void set_bit(uint32_t* reg, uint8_t bit)
{
*reg |= bit;
}
set_bit(my_register , BIT0);
Run Code Online (Sandbox Code Playgroud)
read_temp.c
/* HAL Low-level driver function */
/**
* @brief Read a byte from the I2C bus.
*
* @param address The I2C slave address.
* @return uint8_t The data read from the I2C bus.
*/
uint8_t read(uint8_t address);
Run Code Online (Sandbox Code Playgroud)
test_i2c.cpp
/**
* @brief Read the temperature from the sensor in the board
*
* @return float The temperature in degrees Celsius.
*/
float read_temperature(void);
Run Code Online (Sandbox Code Playgroud)
对于带有测试类的更复杂的测试场景,您可以RESET_FAKE在SetUp()方法中调用。
希望这可以帮助!干杯!