我正在使用遗留代码重构一个巨大的C库,其中许多函数都有参数列表上的指针。我还为新创建的函数编写单元测试,以确保我没有破坏任何东西(除了来自单元测试的所有好东西,这是我的主要动机)。我也不允许更改库的API,只允许更改它下面的代码。
通常我的工作结果看起来像这样(它的专有代码,所以我不能发布实际的例子):
externalApi.h:
/**
* Documentation1
*/
bool someExportedFunction1(uint8_t* buffer, size_t len);
/**
* Documentation2
*/
bool someExportedFunction2();
refactoredCode.h:
/**
* Documentation of internal function1
*/
bool internalFuntion1(uint8_t* buffer, size_t len);
/**
* Documentation of internal function2
*/
bool internalFuntion2(uint8_t* buffer, size_t len);
externalApi.c:
bool someExportedFunction1(uint8_t* buffer, size_t len)
{
if (NULL == buffer)
{
ERROR("Meaningful error log");
return false;
}
if (!internalFunction1(buffer, len))
{
ERROR("Other error log");
return false;
}
if (!internalFunction2(buffer, len))
{
ERROR("Yet another error log");
return false;
}
return true;
}
bool someExportedFunction2()
{
uint8_t lBuffer[10] = {};
if (!internalFunction1(lBuffer, sizeof(lBuffer))
{
ERROR("Interesting error log");
return false;
}
uint8_t* ptr = malloc(10);
if (NULL == ptr)
{
ERROR("Malloc error");
return false;
}
if (!internalFunction2(ptr, 10)
{
free(ptr);
ERROR("Boring error log");
return false;
}
free(ptr);
return true;
}
refactoredCode.c
bool internalFuntion1(uint8_t* buffer, size_t len)
{
if (NULL == buffer)
{
ERROR("Guess what, a meaningful error log");
return false;
}
// Do stuff
return true;
}
bool internalFuntion2(uint8_t* buffer, size_t len)
{
if (NULL == buffer)
{
ERROR("Last meaningful error log");
return false;
}
// Do stuff
return true;
}
这些只是简单的例子来说明问题,同样问题还有无数其他版本。
现在,我编写的所有单元测试包括检查如果我将NULL作为参数传递会发生什么,无论我正在测试多低级别的函数(即使我100%确定NULL也不会传递为一个论点)。
然而,我的一位同事不同意我的说法,NULL是在函数契约之外,正确的方法是编写断言而不是if
s。此类断言不应进行单元测试(即使使用EXPECT_DEATH
宏),因为单元测试也是正确使用函数的文档,并且不允许使用NULL。
我们可以这样总结双方的论点:
Pro“写if
并对其进行单元测试”方法:
if
s,无所谓)?Pro“断言并且不测试”方法:
最后,我们没有人能说服对方,辩论结束时没有得出结论,我们又回到了 Argumentum ab auctoritate 因为另一个人比我更有经验。
但我仍然不相信,所以我在这里问这个问题:
正在考虑我提出的两种方法的所有论据,以及我不知道的所有方法:
是否应该使用if
检查函数合约之外的参数并进行单元测试,或者声明并且不进行测试?
答案 0 :(得分:2)