我最近使用 gTest 测试了 FAT 文件系统和引导加载程序实现(谷歌测试 http://google.github.io/googletest/)对于 Arm Cortex-M3 核心,所以我会留下我的两分钱。
嵌入式软件测试存在的问题是无法通过模拟来复制硬件环境。我想出了三组测试:
A)单元测试(我在TDD https://wingman-sw.com/)运行on my PC。我使用这些测试来开发我的应用程序逻辑。这就是我需要嘲笑/存根的地方。我的公司使用硬件抽象层(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 https://github.com/siu/minunit,这是一个单一的 C 头文件。我已经使用 MinUnit 在 Cortex-M3 内核和专有 DSP 代码上运行了目标测试(未经任何修改)。我在这里也使用了 TDD。
C) 集成测试。我在这里使用 Python 和 Behave 在目标上构建、下载和运行整个应用程序。
回答您的问题:
-
正如其他人已经说过的,从g测试引物 http://google.github.io/googletest/primer.html,并且不用担心嘲笑,只需掌握使用 gTest 的窍门即可。提供一些内存检查(泄漏)的一个很好的替代方案是Cpputest http://cpputest.github.io/。我对派生安装类的 gTest 语法有一点偏好。 Cpputest 可以运行用 gTest 编写的测试。两者都是很棒的框架。
-
I used 假函数框架 https://github.com/meekrosoft/fff用于嘲笑和存根。它使用起来非常简单,并且提供了一个好的模拟框架所期望的一切:设置不同的返回值、传递回调、检查参数调用历史记录等。我想给出Ceedling http://www.throwtheswitch.org/ceedling走吧。到目前为止,FFF 一直很棒。
-
我不这样做。我使用 C++ 编译器(在我的例子中为 g++)编译测试框架和测试,并使用 C 编译器 (gcc) 编译嵌入式代码,然后将它们链接在一起。从下面的示例中,您将看到我没有在 C 文件中包含 C++ 头文件。链接测试时,您将链接除您正在模拟的函数的 C 源文件之外的所有内容。
使用代码管理失败的断言 - 我的驱动程序库中的失败断言,要求系统重置。我如何在测试中模拟这一点?
我会模拟重置功能,添加回调以“重置”您需要的任何内容。
假设您想测试read_temperature
使用的函数read
功能。下面是一个使用 FFF 进行模拟的 gTest 示例。
hal_i2c.h
/* 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);
read_temperature.h
/**
* @brief Read the temperature from the sensor in the board
*
* @return float The temperature in degrees Celsius.
*/
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 at address 0xAB */
raw_value = read(0xAB);
/* Convert the raw value to Celcius */
temp = ((float)raw_value)/0.17+273;
return temp;
}
test_i2c.cpp
#include <gtest/gtest.h>
#include <fff.h>
DEFINE_FFF_GLOBALS;
extern "C"
{
#include <hal_i2c.h>
#include <read_temperature.h>
// Declare the fake C functions using FFF. This needs be inside the extern "C"
// block because we're mocking a C function and the C++ name-mangling would
// break linking otherwise.
// 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 for the fake read() function
RESET_FAKE(read);
// Set the raw temperature value the fake for read should return
read_fake.return_val = 0xAB;
// Make sure that we read 123.4 degrees
ASSERT_EQ((float)123.4, read_temperature());
}
对于带有测试类的更复杂的测试场景,您可以调用RESET_FAKE
in the SetUp()
method.
希望这可以帮助!干杯!