c++ 嵌入式系统的Google测试

6gpjuf90  于 2023-06-07  发布在  Go
关注(0)|答案(2)|浏览(186)

我想使用Google Tests为我的嵌入式应用程序软件编写单元测试。
这些测试将在用C编写的应用软件上执行。应用软件使用的驱动程序(例如I2CSPI),故障Assert用C语言编写。我的问题是:
1.从哪里开始比较好?我指的是我可以阅读的资源,以了解更多关于在嵌入式环境中使用Google Test的信息。
1.我该如何模拟我的驱动程序文件?例如,如果我有一个void read(uint8_t address)函数,在我的I2C库中,我如何模拟这个函数,以便在我的C
类中调用这个特定的函数?
1.这些用C编写的驱动程序文件也包含在我的C文件中。我试着编译一个裸测试文件,只包括我的C类头,并有编译问题,因为编译器找不到驱动程序头。如何避免此问题?
1.使用代码管理失败的Assert-在我的驱动程序库中失败的Assert,需要系统重置。如何在测试中模拟这一点?

ryevplcw

ryevplcw1#

我最近使用gTest(GoogleTest)为Arm Cortex-M3内核测试了一个FAT文件系统和引导加载程序实现,所以我将留下我的两分钱。
嵌入式软件测试存在无法通过模拟来复制硬件环境的问题。我做了三组测试:
A)单元测试(我在TDD中使用的)在我的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,这是一个C头文件。我已经在Cortex-M3内核和专有DSP代码(没有任何修改)上运行了MinUnit的目标测试。我在这里也使用了TDD。
C)集成测试。我在这里使用Python和Behave来构建、下载并在目标上运行整个应用程序。
回答您的问题:
1.正如其他人已经说过的,从gTest Primer开始,不要担心mock,只要掌握使用gTest的窍门就行了。Cpputest是一个很好的替代方案,它提供了一些内存检查(针对泄漏)。我比较喜欢使用gTest语法来派生设置类。Cpputest可以运行用gTest编写的测试。两者都是很好的框架。
1.我使用Fake Function Frakework来模拟和存根。它使用起来非常简单,并且它提供了一个好的mocking框架所期望的一切:设置不同的返回值、传递回调、检查参数调用历史等。我想给予Ceedling一个机会。到目前为止,FFF一直很棒。
1.我不会那么做的我用C编译器(在我的例子中是g)编译测试框架和我的测试,用C编译器(gcc)编译我的嵌入式代码,然后将它们链接在一起。从下面的例子中,你会看到我没有在C文件中包含C++头文件。在链接测试时,除了要模拟的函数的C源文件之外,您将链接所有内容。
使用代码管理失败的Assert-在我的驱动程序库中失败的Assert,需要系统重置。如何在测试中模拟这一点?
我会模仿reset函数,为“reset”添加一个回调函数。
假设您想要测试一个使用read函数的read_temperature函数。下面是一个使用FFF进行模拟的gTest示例。
hal_i2c.h

/* HAL 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>

DEFINE_FFF_GLOBALS;

extern "C"
{
#include <hal_i2c.h>
#include <read_temperature.h>

// 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
    read_fake.return_val = 0xAB;

    // Make sure that we read 123.4 degrees
    ASSERT_EQ((float)123.4, read_temperature());
}

对于包含测试类的更复杂的测试场景,可以在SetUp()方法中调用RESET_FAKE
希望这有帮助!干杯!

cmssoen2

cmssoen22#

1.我不知道有什么特别的参考资料可以用来使用Gtest进行裸机目标测试,但是一般来说,一个好的开始是阅读Gtest Primer,根据你的软件架构,甚至可能是Gmock documentation。后者在测试应用程序相关类而不仅仅是低级驱动程序时可能会很有用。

  • 对此有几种选择。到目前为止,我看到的最常见的一种情况是,目标和运行测试的平台有两种不同的实现。例如,您可能有两个文件
  • ic2.c
  • i2c_x86.cpp

根据您当前是为目标平台还是测试平台进行编译,您可以使用其中的任何一个。
另一种选择是将C实现提升到C++,并为驱动程序编写一个类 Package 器。这将使您受益于C++的功能,并使用依赖注入,继承,CRTP等。

  • 我不太明白你的要求。
  • Gtest有一个ASSERT_DEATH测试,例如我当前的代码库包含以下测试
// 2 byte message does not fit 14bit, assertion triggered
      ASSERT_DEATH(encode_datagram(make_datagram(0, 64, 0)), ".*");

相关问题