CppUTest非常适合嵌入式 C 开发,因为它是唯一允许模拟自由函数(在您的情况下,write_to_i2c()
和read_from_i2c()
)的测试框架。
现在,您应该真正阅读 CppUTest 文档或优秀的书Test Driven Development for Embedded C。
无论如何,下面的代码显示了如何做到这一点。
您的被测单元 (UUT),以可编译的方式编写(下次您提出关于 SO 的问题时,请努力):
#include "temperature.h"
#include "i2c.h"
void somethingA(void) { }
void somethingB(void) { }
void temperature_do(uint8_t address, uint8_t value) {
write_to_i2c(address, value);
const uint8_t tempReadback = read_from_i2c(address);
if (tempReadback == value) {
somethingA();
} else {
somethingB();
}
}
正如您所写,我们需要“伪造”,或者更确切地说,我们需要“模拟”write_to_i2c()
和read_from_i2c()
. 我们将模拟放在一个单独的文件中,比如 i2c_mock.cpp,以便在构建单元测试时,我们链接模拟而不是实际实现:
extern "C" {
#include "i2c.h"
};
#include "CppUTestExt/MockSupport.h"
void write_to_i2c(uint8_t address, uint8_t value) {
mock().actualCall(__FUNCTION__)
.withParameter("address", address)
.withParameter("value", value);
}
uint8_t read_from_i2c(uint8_t address) {
mock().actualCall(__FUNCTION__)
.withParameter("address", address);
uint8_t ret = mock().returnIntValueOrDefault(0);
return ret;
}
有关详细信息,请参阅 CppUMock 文档。这只是经典的 CppUMock 样板。
最后一部分是单元测试:
extern "C" {
#include "temperature.h" // UUT
};
#include "CppUTest/TestHarness.h"
#include "CppUTest/CommandLineTestRunner.h"
#include "CppUTestExt/MockSupport.h"
TEST_GROUP(Temperature)
{
void setup() {}
void teardown() {
mock().checkExpectations();
mock().clear();
}
};
TEST(Temperature, somethingA)
{
const uint8_t value = 10;
mock().ignoreOtherCalls();
mock().expectOneCall("read_from_i2c").ignoreOtherParameters()
.andReturnValue(value);
temperature_do(10, value);
}
TEST(Temperature, somethingB)
{
const uint8_t value = 10;
mock().ignoreOtherCalls();
mock().expectOneCall("read_from_i2c").ignoreOtherParameters()
.andReturnValue(value+1);
temperature_do(10, value);
}
int main(int argc, char** argv) {
return CommandLineTestRunner::RunAllTests(argc, argv);
}
这个 UT 实际上会提供 100% 的分支覆盖率。同样,我无法解释所有细节。如果您观察和比较测试用例somethingA
,somethingB
您将看到需要什么才能使 UUT 一次进入调用路径,somethingA()
一次进入调用路径somethingB()
。
让我们举个例子
mock().expectOneCall("read_from_i2c")
.ignoreOtherParameters()
.andReturnValue(value+1);
在这里,我们对 CppUmock 说期望调用 function read_from_i2c()
,忽略参数是什么,并且,这是最重要的,返回value + 1
(或任何你喜欢的与 不同的东西value
)。这将导致 UUT 进入调用somethingB()
.
快乐的嵌入式 C 开发和快乐的单元测试!