我在《单元测试实施解惑(一)》中指出,使用象Cmockery这样的测试框架,将所需测试的模块通过打桩的方法实施单元测试并不是最有效的方法。在这篇文章中,让我们一同来探索更好的方法。在继续探索之前,让我从传统单元测试开始引入所主张的方法。
图1中所示的分别是某内存池模块(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;
- }
|
图1
为了便于理解,图2示例了一个简化了的桩和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;
-
- mnode.addr_ = 0x5A5A5A5A;
- mnode.in_use_ = false;
-
- g_p_node = &mnode.node_;
- UNITEST_EQUALS (mpool_buffer_alloc (handle), 0x5A5A5A5A);
- g_p_node = 0;
- UNITEST_EQUALS (mpool_buffer_alloc (handle), 0);
- }
|
图2