我在《单元测试实施解惑(一)》中指出,使用象Cmockery这样的测试框架,将所需测试的模块通过打桩的方法实施单元测试并不是有效的方法。在这篇文章中,让我们一同来探索更好的方法。在继续探索之前,让我从传统单元测试开始引入所主张的方法。

  代码所示的分别是某内存池模块(mpool.c)和双向链表模块(dll.c)的代码片断,现在让我们聚焦于为内存池模块的mpool_buffer_alloc函数实施单元测试。由于该函数使用到了双向链表模块的dll_pop_head函数,因此,我们需要对dll_pop_head函数进行打桩。(注:实际上还得对global_interrupt_disable和global_interrupt_enable两函数打桩,但为了简化我们只以dll_pop_head为例)

    mpool.c
    void* mpool_buffer_alloc (mpool_handle_t _handle)
    {
        interrupt_level_t level;
        mpool_node_t *p_node;
        
        level = global_interrupt_disable ();
        if (is_invalid_handle (_handle)) {
            global_interrupt_enable (level);
            return null;
        }
        p_node = (mpool_node_t *)dll_pop_head (&_handle->free_buffer_);
        if (0 == p_node) {
            _handle->stats_nobuf_ ++;
            global_interrupt_enable (level);
            return null;
        }
        global_interrupt_enable (level);
        p_node->in_use_ = true;
        return (void *)p_node->addr_;
    }
    dll.c
    dll_node_t *dll_pop_head (dll_t *_p_dll)
    {
        dll_node_t *p_node = _p_dll->head_;
        if (p_node != 0) {
            _p_dll->count_--;
            _p_dll->head_ = p_node->next_;
            if (0 == _p_dll->head_) {
                _p_dll->tail_ = 0;
            }
            else {
                p_node->next_->prev_ = 0;
            }
            p_node->next_ = 0;
            p_node->prev_ = 0;
        }
        
        return p_node;
    }

  为了便于理解,下面代码示例了一个简化了的桩和mpool_buffer_alloc函数的测试用例。请注意,测试用例中的handle实参假设之前通过mpool_init函数所获得,代码中同样为了简化并未列出。

    stub_dll.c
    dll_node_t *g_p_node;
    dll_node_t *dll_pop_head (dll_t *_p_dll)
    {
        return g_p_node;
    }
    test_mpool.c
    void test_mpool_buffer_alloc ()
    {
        mpool_node_t mnode;
        // set up test environment
        mnode.addr_ = 0x5A5A5A5A;
        mnode.in_use_ = false;
        // do test
        g_p_node = &mnode.node_;
        UNITEST_EQUALS (mpool_buffer_alloc (handle), 0x5A5A5A5A);
        g_p_node = 0;
        UNITEST_EQUALS (mpool_buffer_alloc (handle), 0);
    }