如何在这个malloc包装上跟踪TDD测试失败?

我正在尝试使用TDD和C.我想按照TDD方法编写一个简单的malloc包装器。 我试图遵循Bob Martin的TDD三法则

  1. 除非要进行失败的unit testing通过,否则不要编写生产代码。
  2. 不要写更多的unit testing而不是足以失败,构建失败就是失败。
  3. 不要写更多的生产代码,而不是足以通过一个失败的unit testing。

这是我的代码到现在为止:

J_STATUS MemAlloc(long Size, void **OutPtr) { J_STATUS ReturnStatus; void *Ptr; Ptr = NULL; if (Size >= 0) { Ptr = malloc(Size); *OutPtr = Ptr; ReturnStatus = SUCCESS; //TODO controllare malloc error } else { ReturnStatus = ERROR; } return ReturnStatus; } 

这些是我的测试(我使用的是Unity测试框架):

  #include "../unity/unity.h" #include "../src/jMem.h" #include "stdlib.h" static int *ptr; static J_STATUS Result; static long Size; static long Count; void setUp() { ptr = NULL; Size = 10; Count = 5; } void tearDown() { if (ptr != NULL) { free(ptr); } } void test_MemAllocShouldAllocateMemoryAndReturnSuccess(void) { Result = MemAlloc(Size, (void **) &ptr); TEST_ASSERT_EQUAL(SUCCESS, Result); TEST_ASSERT_NOT_NULL(ptr); } void test_MemAllocShouldReturnErrorIfSizeIsNegative(void) { Size = -4; Result = MemAlloc(Size, (void **) &ptr); TEST_ASSERT_EQUAL(ERROR, Result); } 

现在我如何为失败的malloc编写测试? 我知道我可以链接一个不同版本的malloc或者我可以编写一个宏来使malloc失败,但这应该只适用于这个测试,而在另一个我想使用真正的malloc。

任何的想法? 谢谢

要替换malloc()您可以使用如下函数指针:

 void* (*pMalloc)(size_t size) 

然后你使用pMalloc(some size)而不是malloc(some size)

如果将指针设置为指向malloc() ,则在pMalloc = &malloc; ,你将使用真正的malloc() 。 如果将其设置为指向您自己的function,您将使用您的function。

在您的函数中,您可以通过返回NULL来模拟malloc()失败。 如果测试需要,您也可以从中调用malloc()

同样,你可以替换你自己的free()

在这些替代函数中,您可以记录正在发生的事情。

例如,如果你将LONG_MAX传递给你的包装函数(我假设它仍然将大小作为一个long参数),你的假malloc()可以记录它实际收到的大小。 在测试中,您可以注意到LONG_MAX已从包装器传播到malloc()(size_t)LONG_MAX ,如果size_t短于long (就位数而言),您可以通过比较值来检测差异( LONG_MAX != (size_t)LONG_MAX )。

您还可以记录伪malloc()返回的指针值(从内部返回),并将其与包装器返回的值进行比较,看看它们是否不同。

你可以进一步详细说明这个想法来提出更多的测试(例如,包装器返回一个非NULL值,但假的malloc() (因此真正的一个)根本不会被调用)。

在完成所有这些操作时,您不必登录文件或stdout 。 您可以为此目的专门设计数据结构,然后进行检查。

也许这是一个你不需要测试的边缘情况。 只需编写代码。 🙂

另一方面,由于我们都喜欢练习良好的TDD,另一种选择是将malloc调用提升为函数参数。 作为函数指针,您可以在一次测试中传递常规malloc的地址, malloc_that_errors在错误测试用例中malloc_that_errors的地址。

请注意,它会污染您的包装器的界面,这是我个人不喜欢的。 它打破了你想要的抽象,我讨厌在任何地方传递malloc 。 鉴于此,我会回去不测试它。 😀

希望有所帮助!

布兰登

当malloc包装器使用long而不是size_t作为size参数时,它不是很有说服力。

你应该能够通过分配一个非常大的内存使其失败,使用size_t并找到最大值。 大多数真正的malloc()都不会成功分配,因此它们会失败。

当然,这仍然是让它失败的一种相当假的方式,我想唯一合适的方法是提供钩子,这样你就可以替换不同的malloc()

老问题,但我觉得,根据你的编译器,有一个答案缺失。 GNU LD有一个--wrap symbol选项,允许您在链接时覆盖某些函数。

 void * __wrap_malloc (int c) { printf ("malloc called with %ld\n", c); return __real_malloc (c); } 

与此代码一样,每次调用malloc都将被调用__wrap_malloc ,如果你想调用真正的malloc,它可以通过__real_malloc

这个function可能与其他链接器一样,我不知道,但对于GCC来说,这是一个不错的选择。