嵌入式系统的 Google 测试

mmc*_*lk1 4 c c++ embedded unit-testing googletest

我想使用 Google Tests 为我的嵌入式应用程序软件编写单元测试。

这些测试将在用 C++ 编写的应用软件上执行。I2C应用软件(例如, )使用的驱动程序SPI,故障断言是用 C 编写的。我的问题是:

  1. 什么是一个好的起点?我的意思是我可以阅读的资源,以了解有关在嵌入式环境中使用 Google Test 的更多信息。
  2. 我该如何模拟我的驱动程序文件?例如,如果void read(uint8_t address)我的 I2C 库中有一个函数,我该如何模拟该函数,以便在我的 C++ 类中调用该特定函数?
  3. 这些用 C 编写的驱动程序文件也包含在我的 C++ 文件中。我尝试编译一个裸测试文件,仅包含我的 C++ 类头,但遇到了编译问题,因为编译器找不到驱动程序头。我怎样才能避免这个问题?
  4. 使用代码管理失败的断言 - 我的驱动程序库中的失败断言,要求系统重置。我如何在测试中模拟这一点?

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 在目标上构建、下载和运行整个应用程序。

回答您的问题:

  1. 正如其他人已经说过的,从gTest Primer开始,不要担心嘲笑,只需掌握使用 gTest 的窍门即可。提供一些内存检查(泄漏)的一个很好的替代方案是Cpputest。我对派生安装类的 gTest 语法有一点偏好。Cpputest 可以运行用 gTest 编写的测试。两者都是很棒的框架。

  2. 我使用Fake Function Frakework进行模拟和存根。它使用起来非常简单,并且提供了一个好的模拟框架所期望的一切:设置不同的返回值、传递回调、检查参数调用历史记录等。我想尝试一下Ceedling。到目前为止,FFF 一直很棒。

  3. 我不这样做。我使用 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_FAKESetUp()方法中调用。

希望这可以帮助!干杯!