近两天一有空就会去看看项目代码,思考一下如何利用cmockery对项目里已有的代码进行测试。项目代码中很多被调用的接口都带有输出参数,而且在这些接口中多利用返回值指示执行成败也否,而利用输出参数返回一些关键结果,这些结果值甚至影响着后续的函数执行流程。前期研究cmockery时没有注意到cmockery是否可以设置被mock接口的输出参数的值,不过回顾了一下cmockery实现的原理,觉得cmockery是应该可以支持的。遂重新翻看了一下cmockery的manual,发现在mock_query_database中确有对输出参数的mock调用,代码如下:

// Mock query database function.
unsigned int mock_query_database( DatabaseConnection* const connection,
                                 const char * const query_string, void *** const results)
{
 *results = (void**)mock();
 return (unsigned int)mock();
}

void test_get_customer_id_by_name(void **state) {
 DatabaseConnection connection = { "somedatabase.somewhere.com", 12345678, mock_query_database };
 int customer_ids = 543;
 will_return(mock_query_database, &customer_ids);
 will_return(mock_query_database, 1);
 assert_int_equal(get_customer_id_by_name(&connection, "john doe"), 543);
}

上面代码在test_get_customer_id_by_name中两次针对被mock的接口mock_query_database调用will_return,实际上是在符号“mock_query_database”对应的value list里插入了两个item,第一个item的值为&customer_ids,第二个为1。当测试执行到mock_query_database的第一个mock时,返回第一个item:&customer_ids,执行到第二个mock时返回第二个item的值1。这样在测试过程中设置输出参数值的目的就达到了。

在使用cmockery时唯一需要你关注的就是will_return设置的顺序和在被mock接口中调用mock的顺序,切记不要弄反了。

这里再举一个例子,再直观感受一下:

/* message_handler.c */

#include

extern int dispatch_message(void *msg);
extern int get_next_message(void **msg);

int handle_next_message() {
        void *  temp_msg        = NULL;
        int     rv              = 0;           

        rv = get_next_message(&temp_msg);
        if (!rv) {                     
                if (!temp_msg) {                               
                        return -1;
                } else {                                                               
                        return dispatch_message(temp_msg);
                }                                                                                              

        }                                                              

        return rv;
}

/* test_message_handler.c */
#include
#include
#include
#include "cmockery.h"
#include

int dispatch_message(void *msg) {
        return 0;
}

int get_next_message(void **msg) {
        (*msg) = (void*)mock();
        return (int)mock();
}

extern int handle_next_message();

void test_handle_next_message_success(void **state) {
        will_return(get_next_message, 0×1234);
        will_return(get_next_message, 0);
        assert_true(handle_next_message() == 0);
}

void test_handle_next_message_fail(void **state) {
        will_return(get_next_message, NULL);
        will_return(get_next_message, 0);
        assert_true(handle_next_message() == -1);
}

int main() {
        const UnitTest tests[] = {
                unit_test(test_handle_next_message_success),
                unit_test(test_handle_next_message_fail)
        };
        return run_tests(tests);
}

执行结果:
test_handle_next_message_success: Starting test
test_handle_next_message_success: Test completed successfully.
test_handle_next_message_fail: Starting test
test_handle_next_message_fail: Test completed successfully.
All 2 tests passed

© 2009, bigwhite. 版权所有.

Related posts:

  1. C单元测试之使用cmockery
  2. C单元测试之Mock Test篇
  3. C单元测试包设计与实现
  4. 再谈Mock Object
  5. 关于宏定义切换以及屏蔽的例子