嵌入式C中的单元测试-测试引用SDK的模块



我用C语言编程,为微控制器创建了一个项目。由于它的特殊性(一个内置BLE内核的微控制器),我必须使用SDK和特定的项目模板。当我的模块对SDK中的其他文件(模块)有大量引用时,我如何测试它们?(使用函数需要引用,例如,通过BLE发送数据)我必须以某种方式模拟每个SDK函数吗?我使用的是Unity测试框架。模块示例:

my_module.c

#include "sdk_module_1.h"
#include "my_module.h"
void init_hardware(void)
{
//function code
}
bool send_data(int data)
{ 
//prepare data eq.
data++ 
//send data using SDK function (sdk_module_1.h)
return send_data(data);
}

my_module.h

void init_hardware(void)
void send_data(int data)

my_module_test.c

#include "my_module.h"
#include  "//unity files" 
TEST_SETUP(Test)
{   
}
TEST_TEAR_DOWN(Test)
{
}
TEST(Test, First_test)
{
TEST_ASSERT_EQUAL(send_data(5),true);
}

当我尝试测试我的模块时,我在引用SDK模块及其函数时遇到了问题。如何为此类软件创建测试?我应该更改模块的编写方式吗?

您想要的资源是James Grenning的嵌入式C测试驱动开发。

(注意:下面是我的想法翻译成C。如果你发现与Grenning的方法有冲突,请先试试他的方法——他在嵌入式空间里的圈数比我多得多。)


当我的模块对SDK中的其他文件(模块)有大量引用时,我如何测试它们?

有时答案是你必须改变你的设计。换句话说,将可测试性视为设计约束,而不是事后考虑。

我通常这样描述它:我们希望设计我们的代码,以便(a)所有复杂的代码都很容易测试,(b)任何难以测试的代码都是"测试";如此简单显然没有不足之处";。

这通常意味着设计我们的代码,以便复杂代码和难以测试的代码之间的协作是可配置的,从而允许您在使用真实代码时提供替代实现(stub/mock/test-double)。

因此,与其让A(复杂)直接调用B(难以测试),不如让A通过函数指针调用B,在测试过程中,可以用指向更简单函数的指针代替它。

(在某些风格中,情况正好相反:默认情况下,复杂的逻辑指向惰性/存根实现,而您选择使用复杂的实现。)

换句话说,我们取代

void A(void) {
B(); // B is the function that makes things hard to test.
}

带有

void A(void) {
C(&B);
}
# It's been a long time, please forgive (or correct) the spelling here
void C( void (*fn)()) {
&fn();
}

我们通过观察A来测试它,同意它是"A";如此简单显然没有缺陷";,我们通过将C的指针传递给替代实现来测试C,为B编写尽可能多的替代,以确保覆盖C的所有边缘情况。


注意;"难以测试";可以覆盖很多基础-实际函数很慢,实际函数不稳定,实际函数需要花钱。。。如果它让测试变得不那么愉快或不那么有效,那么它就很重要。

首先,我强烈建议使用Ceedling在C中管理和运行单元测试。它非常好地封装了Unity和CMock,使单元测试,特别是嵌入式系统的单元测试,比其他情况下容易得多。

关于涉及SDK时的单元测试,您首先需要记住,单元测试的目的是测试一个单元,因此除此之外的任何东西都需要被嘲笑或存根,否则它就不是单元测试,不能作为一个单元运行。

因此,对于I2C硬件抽象模块这样的东西,对底层SDK的任何调用(通常会进行实际的I2C事务)都需要进行模拟,这样你就可以对调用而不是实际调用时应该发生的事情抱有期望。通过这种方式,I2C HAL模块的单元测试只测试其行为和处理调用的方式,而不测试其他任何内容。

因此,使用SDK进行单元测试通常需要做的就是确保您使用的任何模块的公共函数都被嘲笑。然后,您只需在测试中包含mock,而不是真正的模块。

现在,我发现你并不真的想为了单元测试的目的而破坏原始的SDK及其结构,而且你也不想改变真实代码包括真实模块的方式,所以当你在代码中调用的SDK函数位于SDK中其他模块的层之后时,测试的问题就来了,这是经常发生的情况。在这里,您不想模拟您没有使用的所有内容,但您确实需要匹配include结构。我发现,一个很好的方法就是创建一个support文件夹,并将代码从SDK中使用的任何顶级标头复制到其中。然后,我将测试配置为在从SDK中包含之前包含upport

一旦有了这种结构,使用SDK进行单元测试就很容易了。Ceedling将帮助解决这一切。

最新更新