2009年八月月 发布的文章

手机做MODEM实现无线上网

前不久和一位售前同事到北京出差,途中在动车上看到他把本子连到Nokia手机上并通过手机上网。现如今通过手机上网也不是什么新鲜事,关键看是否需要。平时上班有公司网络,下班家里有宽带,路途中可通过手机直接浏览WAP站点,所以对于我这样的开发人员而言倒是没有特别充分的让本子通过做MODEM的手机上网的需求。

公司信息安全改造屏蔽了外部的许多站点,其中包括BLOGBUS,而且目前BUS不支持WAP浏览。这让我在白天闲暇时维护自己的博客成为了一件困难事,就这样我也有了通过手机MODEM实现电脑无线上网的需求。当然出差途中也可以通过这种方式处理一些紧急事件。

我的手机现在看来可能已经有些过时了 — 2006年入手的摩托罗拉A780。平时手机最常用的功能就是电话和WAP上网浏览,自从购进后也没有深入探究其各种功能和配置,甚至还未尝试将数据下载到电脑中进行备份,所以如何将A780设置为MODEM并实现本子上网我就更无从知晓了。

不过还好有伟大的Google,搜索了一下,翻阅了多篇网友们的心得体会,感觉都甚是复杂。我先不管那么多了,先让本子认识一下A780这个无线MODEM吧 — 安装一个Motorola USB Driver,一切顺利,可以在“控制面板->“电话和调制解调器”看到我的A780已经成为了一只“MODEM"了。但是接下来按照网上提示的方法去设置和拨号却无论如何也无法连通,尝试了几次后,放弃。

在各种关于A780的资料中,大家都提到了一个摩托手机必备之工具-"motorola phone tools, 简称mpt",该工具能实现手机与电脑的数据同步,甚至可以通过本子编辑短信并发送,这次我也下载一个吧(太落伍了,居然买机若干年后才安装如此"知名"的工具!)。

MPT的包挺大,70多M。而且包内也带有“Motorola USB Driver”。执行之。在左侧的功能列表栏中,功能还是蛮齐全的。试了试联系人、SMS等功能都还好用,就是速度有些慢,也许我的联系人有些多,从手机读取到电脑上竟用了很长时间。

这些功能虽好,但不是我这次想要的。看看是否有我想要的连接Internet的功能呢?在“通讯”->"Internet"中果然看到了希望。点击后,一个生动的界面出现在眼前。点击“管理连接”,弹出窗口让你选择是通过CMWAP还是直连Internet,我首先选了CMWAP,之后点击“连接”,十几秒后提示连接成功。在界面的流量数据中已经有上下行流量变化了。打开IE尝试访问了一下新浪的WAP站点,没有结果,有些失望。断开连接后,再次配置链接属性,这次选择直连Internet,重复以上步骤。在浏览器里输入google的url,若干秒后google的首页就呈现在面前了,上网设置成功。此时我的第一感受就是:这也没有网上很多资料里说的那么复杂啊。不过你要小心了,如果你没有开通移动运营商的无线上网包月套餐业务的话,上网的费用还是相当贵的,所以还是要谨慎点击“连接”。

回想一下,CMWAP之所以不好用,也许跟我的浏览器中公司代理的设置有关,不管了,反正也不用。

cmockery支持mock输出参数

近两天一有空就会去看看项目代码,思考一下如何利用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

C单元测试之使用cmockery

这么久以来一直没有找到一款很好的支持mock测试的C语言单元测试工具包,但前不久在一网友的评论中得知:去年Google曾发布了一款c语言的轻量级单元测试framework — “cmockery”,cmcokery很小巧,对其他开源包没有依赖,对被测试代码侵入性小;它支持mock test,同样也可以支持常规的单元测试。

之前在博客中曾描述过C语言实现mock的一个思路,不过和cmockery对比起来,当时我的思路显然还处于初级阶段,而cmockery则走到了更高级,使用起来也更为简便。

还是以我上一篇文章中的代码来举例,利用cmockery来对biz.c进行mock test。

应用层被测试代码不变:
/* biz.h */
#ifndef BIZ_H
#define BIZ_H

#include

int biz_operation(char *fname);
#endif

/* biz.c */
#include "biz.h"

int biz_operation(char *fname) {
        FILE *fp = NULL;

        fp = fopen(fname, "r");
        if (fp == NULL) {
                printf("fail to open fle!\n");
                return 1;
        } else {
                printf("succeed to open file!\n");
                return 0;
        }
}

利用cmockery改造测试代码如下:
/* test.c */
#include
#include
#include
#include
#include "cmockery.h"
#include "biz.h"

FILE *fopen(const char *filename, const char *mode) {
        return (FILE*)mock();
}

void test_biz_operation_return_succ(void **state) {
        will_return(fopen, 0×1234);
        assert_true(biz_operation("foo.txt") == 0);
}

void test_biz_operation_return_fail(void **state) {
        will_return(fopen, NULL);
        assert_true(biz_operation("foo.txt") == 1);
}

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

gcc  biz.c test.c -I ./ -I {YOUR_CMOCKERY_INSTALL_DIR}/include/google -L {YOUR_CMOCKERY_INSTALL_DIR}/lib -lcmockery

执行a.out结果如下:
test_biz_operation_return_succ: Starting test
succeed to open file!
test_biz_operation_return_succ: Test completed successfully.
test_biz_operation_return_fail: Starting test
fail to open fle!
test_biz_operation_return_fail: Test completed successfully.
All 2 tests passed

在测试代码中override了C标准库中fopen的实现,代码很简单,就是调用一个mock,然后根据返回值类型做一个强制类型转换。那么执行
起来后mock究竟会返回什么值呢?这个值你可以任意设定,看到下面test_biz_operation_return_xx中的
will_return宏了吗,在will_return中你可以设定fopen中mock()调用的返回值:will_return(fopen,
0×1234)或will_return(fopen, NULL);
是不是思路清晰了许多了呢,没错。cmockery就是通过在will_return中设置mock的返回值,并在执行fopen这类被mock的函数接
口中通过接口实现中的mock调用将你设定的值返回出来,从而控制被测试接口biz_operation的执行路径和执行结果,以达到mock
test的目的。

cmockery的源代码行数不到3K,你阅读一下will_return和mock的源代码就一目了然了。大致思路应
该是:在堆上分配一块内存,用来存储你在will_return中设定的返回值,用函数名字符串做索引;在mock()中则通过调用mock的函数的名字
去匹配,得到已经设定好的存储在堆内存上的那个值,并在转型后返回。

以上是cmockery支持的对通用函数返回值的
mock,cmockery还支持mock function的parameters checking、setup和teardown、assert
failure和dynamic memory allocation的mock等,cmockery的manual
有细致说明。不过原理都类似,在上面也都说过了。不过有些机制对被测试代码还是有一定侵入性的。cmockery虽好,但是毕竟是针对C语言的,就无法逃
过链接阶段符号resolved的问题,测试上面简单的代码还好,如果测试大工程中某复杂源代码文件中的接口时,链接时就会有些麻烦,所以工程源代码组织
的扁平化、接口功能单一化、包含和清晰的调用关系都会大大有助于实施mock单元测试,否则一旦你的biz_operation接口功能很复杂,调用了很
多其他接口,实现很冗长的话,那么你的mock测试同样也会很麻烦,这时你需要做的就应该是重构你的代码,将复杂的biz_operation拆分开了。

另外cmockery的安装很简单,遵循常规的configure->make->make install,这里不再贽述了。




这里是Tony Bai的个人Blog,欢迎访问、订阅和留言!订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您喜欢通过微信App浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:



本站Powered by Digital Ocean VPS。

选择Digital Ocean VPS主机,即可获得10美元现金充值,可免费使用两个月哟!

著名主机提供商Linode 10$优惠码:linode10,在这里注册即可免费获得。

阿里云推荐码:1WFZ0V立享9折!

View Tony Bai's profile on LinkedIn


文章

评论

  • 正在加载...

分类

标签

归档











更多