我最近使用 gTest 为 Arm Cortex-M3 内核测试了 FAT 文件系统和引导加载程序实现,所以我会留下两分钱。
嵌入式软件测试存在无法通过模拟复制硬件环境的问题。我想出了三组测试:
A) 在我的 PC 上运行的单元测试(我在 TDD 中使用)。我使用这些测试来开发我的应用程序逻辑。这是我需要模拟/存根的地方。我的公司使用硬件抽象层 (HAL),这就是我模拟的。如果你想编写可测试的代码,最后一点是基础。
/* this is not testable */
my_register->bit0 = 1;
/* this is also not testable */
*my_register |= BIT0;
不要做直接寄存器访问,使用一个可以模拟的简单 HAL 包装函数:
/* this is testable */
void set_bit(uint32_t* reg, uint8_t bit)
{
*reg |= bit;
}
set_bit(my_register , BIT0);
后者是可测试的,因为您要模拟 set_bit 函数,从而打破对硬件的依赖。
B) 对目标的单元测试。这是一组比 (A) 小得多的测试,但它仍然很有用,特别是对于测试驱动程序和 HAL 功能。这些测试背后的想法是我可以正确地测试我将模拟的函数。因为它在目标上运行,所以我需要它尽可能简单和轻量,所以我使用MinUnit,它是一个单独的 C 头文件。我已经使用 MinUnit 在 Cortex-M3 内核和专有 DSP 代码上运行了目标测试(没有任何修改)。我这里也用过 TDD。
C) 集成测试。我在这里使用 Python 和 Behave 在目标上构建、下载和运行整个应用程序。
回答您的问题:
-
正如其他人已经说过的,从gTest Primer 开始,不要担心嘲笑,只要掌握使用gTest 的窍门。 Cpputest 是提供一些内存检查(针对泄漏)的好选择。我对派生设置类的 gTest 语法有一点偏好。 Cpputest 可以运行用 gTest 编写的测试。两者都是很棒的框架。
-
我使用Fake Function Frakework 进行模拟和存根。它使用起来非常简单,它提供了一个好的模拟框架所期望的一切:设置不同的返回值、传递回调、检查参数调用历史等。我想试试Ceedling。到目前为止,FFF 一直很棒。
-
我不这样做。我用 C++ 编译器(在我的例子中是 g++)编译测试框架和我的测试,用 C 编译器(gcc)编译我的嵌入式代码,然后将它们链接在一起。从下面的示例中,您会看到我没有在 C 文件中包含 C++ 头文件。链接测试时,除了要模拟的函数的 C 源文件之外,您将链接所有内容。
使用代码管理失败的断言 - 我的驱动程序库中的失败断言需要系统重置。如何在测试中模拟这一点?
我会模拟重置功能,添加一个回调来“重置”你需要的任何东西。
假设您要测试使用read 函数的read_temperature 函数。下面是一个使用 FFF 进行模拟的 gTest 示例。
hal_i2c.h
/* Low-level driver function */
uint8_t read(uint8_t address);
read_temperature.h
/* Reads the temperature from the I2C sensor */
float read_temperature(void);
read_temp.c
#include <hal_i2c.h>
float read_temperature(void)
{
unit8_t raw_value;
float temp;
/* Read the raw value from the I2C sensor */
raw_value = read(0xAB);
/* Convert the raw value */
temp = ((float)raw_value)/0.17+273;
return temp;
}
test_i2c.cpp
#include <gtest/gtest.h>
#include <fff.h>
extern "C"
{
#include <hal_i2c.h>
#include <read_temperature.h>
}
DEFINE_FFF_GLOBALS;
// Create a mock for the uint8_t read(uint8_t address) function
FAKE_VALUE_FUNC(uint8_t , read, uint8_t);
TEST(I2CTest, test_read) {
// This clears the FFF counters
RESET_FAKE(read);
// Set the raw temperature value
read_fake.return_val = 0xAB;
// Make sure that we read 123.4 degrees
ASSERT_EQ((float)123.4, read_temperature());
}
希望这会有所帮助!干杯!