2

我正在试验 TDD 和 C。我想按照 TDD 方法编写一个简单的 malloc 包装器。我正在尝试遵循 Bob Martin 的 TDD 三大定律

  1. 不要编写生产代码,除非它是为了使失败的单元测试通过。
  2. 不要编写超过足以导致失败的单元测试,构建失败就是失败。
  3. 不要编写超过足以通过一个失败的单元测试的生产代码。

到目前为止,这是我的代码:

    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。

任何的想法?谢谢

4

4 回答 4

1

也许这是您不需要测试的边缘情况。只写代码。:-)

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

请注意,它弄脏了包装器的界面,我个人不喜欢。它打破了你想要的抽象,我不想malloc到处传递。鉴于此,我会回去不测试它。:-D

希望有帮助!

布兰登

于 2012-09-21T00:53:41.053 回答
1

作为替代malloc(),您可以使用如下函数指针:

void* (*pMalloc)(size_t size)

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

如果您将指针设置为指向malloc(),如在 中pMalloc = &malloc;,您将使用真正的malloc(). 如果您将其设置为指向您自己的功能,您将使用您的该功能。

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

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

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

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

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

您可以进一步阐述这个想法以提出更多测试(例如,包装器返回一个非 NULL 值,但malloc()根本不调用假的(因此真正的))。

在执行所有这些操作时,您不必登录到文件或stdout. 您可以为此目的专用一个数据结构,然后对其进行检查。

于 2012-09-21T02:35:27.583 回答
0

long您的 malloc 包装器在使用而不是size_tsize 参数时不是很有说服力。

您应该能够通过分配非常大量的内存来使其失败,使用size_t并找到最大值。大多数 real malloc()s 不会成功分配它,因此它们会失败。

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

于 2012-09-20T10:11:45.227 回答
0

老问题,但我觉得,根据您的编译器,缺少一个答案。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.

我不知道其他链接器也可能提供此功能,但对于 GCC,这是一个不错的选择。

于 2015-10-07T06:08:13.247 回答