一、背景

目前市场上有很多集成了各种功能的 SDK,开发者只需要调用 SDK 中提供的 API 就能实现一些原本实现起来比较复杂的功能,不需要关心怎样去跟 server 通信,也不用搭建自己的后端 server。我认为 SDK 这个东西秉承了 “让专业的人去做专业的事” 的精神,对开发者来说在某种程度上提供了很大的便利。

最近研究了下 iOS SDK 的接口测试,进行了总结。如有不对的地方,望不吝指正呀~

二、从客户端到 SDK 接口测试的几点感悟

  1. 要知道你要测试的到底是什么。
    之前一直是通过 IM 类客户端来测试 SDK 的接口调用,很多功能的执行结果判断起来非常直观。举个例子:在测试发送 1 条消息给某人,从客户端的角度很容易判断这个 case 通过没有,只要看看界面有没有对应的消息就可以了。但是当我从 sdk 接口测试的角度去判断时,一下子有点蒙蔽了,完全不知道怎么判断这个 case 是否通过(请原谅那时我还是个 OC 小白)。 针对上面那个问题,可以将其拆分成两步:step1:用对应的开发语言去实现 “发送消息” 这个过程。step2:判断消息是否发送成功。第一步通过学习 OC 的基本知识就可以实现,而第二步需要了解一些客户端的开发常识以及 SDK 的使用方法,自己随后补充了下这方面的知识,了解到这个过程是这样的,如下图所示:
    客户端调用了 “发送消息这个接口”,将该条消息发给服务器;服务器收到后进行处理,处理成功后给客户端一个回包;这个回包触发了客户端的 “消息是否成功发送” 这个回调函数,客户端在这个回调中解析服务器的回包,例如解析出返回码是成功的,就在界面 UI 把发送的这条消息渲染出来,至此完成了整个过程。所以在这个 case 中我们需要关注,在收到服务器的回包后,相应的回调是否被触发,且服务器返回的回包数据是否与你期望的一致。
    客户端的很多功能的实现大部分都是这种方式(当然图中是最简单的方式,还有复杂的情况,例如多回调触发,回调多次被触发等),因此要做好 SDK 接口测试,需要准备相应开发语言的知识,并且了解 SDK 的的使用方法,所以自己尝试去用 SDK 开发一个 demo 是最好不过了!
  2. 设计测试用例的思路转变。
    客户端的测试用例很多时候从上至下考虑的,即主要从用户的使用层面去设计测试用例,一开始我在设计接口测试用例的时候也是通过写代码构造各种场景来判断接口调用是否正确,但是随后发现忽略了接口本身,而且应该去了解一下接口的内部代码,针对接口的实现去设计一些 case(这块我也在学习中,先把观点抛出来,[二哈]),例如参数检查等等,换句话说就是测试用例的设计加入从下到上的部分。同时最近读了论坛里@chenhengjie123接口测试的一些感悟 受益匪浅,大家可以读下,再此就不再赘述。说实话感觉 SDK 接口测试在某种程度上跟服务器的接口测试有很多值得借鉴的地方(原谅我是个服务器接口测试的小菜鸟)~
  3. 接口测试并非万能的。
    接口测试在很大程度上能够解放我们的双手,减轻手动测试的负担,但是一些测试还是需要借助手工测试来完成,比如一些异常情况,弱网下的表现,网络切换等等,所以我们要做的是:能够用 sdk 接口测试完成的,我们坚决不动手,但也不能过分依赖 SDK 接口测试,而忽略了全面的测试指标。

三、测试 Tips

3.1 异步测试方法

异步方法不像 require 方法那么容易上手,在文章最上面的那张图示中,当服务器会包返回给 app 时,被触发的回调就是异步函数,这个函数不会阻塞线程,而且什么时候被触发完全取决于服务器回包的时间间隔,所以当我们测试该类的方法时,需要满足这样的场景:当我们的回调函数被触发时,测试用例的代码才会结束执行。测试异步函数的方法也有很多,我这里采用的是给予 xctest 框架提供的 ‘expectationForNotification’ 和 ‘waitForExpectationsWithTimeout’ 函数以及 ios 的通知中心功能的一些函数来实现的,见如下代码:

- (void)testSendtextmessage{
    NSString *text=@"文本消息";
    //构造文本消息
    NIMSession *session=[NIMSession session:_advanceTeamWithSendMessage type:           NIMSessionTypeTeam];
    NIMMessage *message=[[NIMMessage alloc] init];
    message.text=text;

   //调用发送消息的API
    [_chatmanager sendMessage:message toSession:session error:nil];

   //监听ios通知中心是否有内容为'SendMassageSuccess'通知消息
    [self expectationForNotification:@"SendMassageSuccess" object:nil handler:nil];

  //监听的行为最多持续60s,60s之内收到了该条通知消息,测试执行直接结束。60s之内没有受到则说明有问题
    [self waitForExpectationsWithTimeout:60 handler:nil];
}
//异步回调函数
- (void)sendMessage:(NIMMessage *)message didCompleteWithError:(NSError *)error
{
    //此处可以添加一些对服务器回包数据的判断代码
    //向ios通知中心发送内容为'SendMassageSuccess'通知消息
    [[NSNotificationCenter defaultCenter] postNotificationName:@"SendMassageSuccess" object:nil];
}

如代码中的注释所解释的那样,我们在异步函数中做了一件事情,就是向 ios 通知中心的发送消息,而在测试代码的 case 中去针对这个通知消息做异步等待,并设置一个最大的超时时间,以此来实现判断异步函数是否被正确的触发以及服务器数据回包是否正确。

3.2 巧用 random

作为一个 tester,我们大量的工作就是测试各种不同的场景结果是否符合预期,一套参数组合就是一个测试用例。转换到 SDK 的接口测试,我们可以用一种比较巧妙的办法,避免一种参数组合写一个测试 case,而是通过循环与 random 的结合,达到同样的效果。

举个例子我在测试中,有一个 “创建高级群” 的接口,这个接口有 2 个参数(在此只拿 2 个参数为例,实际情况下更多):参数 1-申请入群的权限(枚举值 0-直接申请即可加入,1-申请后需管理员同意才可加入),参数 2-修改群资料的权限(枚举值 0-仅群主可修改,1-群主和管理员可修改,2-所有人均可修改)。这样这个接口就有 2X3=6 中参数的组合情况,最直接最暴力的方法是写 6 个结构类似的测试 case,保障这个接口是 OK 的,但是这样会造成代码很多很冗余,正如题目所述,可以利用 random 这个特性来搞些事情。如下图所示:

- (void)testCreateAdvancedTeam
{
    NSString *userId = [[[NIMSDK sharedSDK] loginManager] currentAccount];
    NSArray *users = @[userId];

    NSMutableArray *teamIds = [NSMutableArray array];
    //利用循环+random构造case
    for (NSInteger index = 0; index < 6; index++)
    {
        //构建群时所需要的参数
        NIMCreateTeamOption *option = [[NIMCreateTeamOption alloc] init];
        //申请入群的权限的参数在0和1之间随机产生
        option.joinMode = arc4random() % 2;
        //修改群资料权限的参数在0、1、2之间随机产生
        option.updateInfoMode = arc4random() % 3;
        //调用创见高级群的API
        [[[NIMSDK sharedSDK] teamManager] createTeam:option
                                               users:users
                                          completion:^(NSError *error, NSString *teamId) {                                             
                                           //一些对返回结果的判断代码
                                          }];   
    }
}

上述代码就是通过做循环+每次随机产生随机参数的模式来减少代码量,从严格意义来讲并不是安全严谨的,但是若参数组合有 12 种情况,其中一个参数每次都没被随机到的概率为 1/12,而且我们的测试工程代码并不是仅仅跑一次,可能会跑很多次,这样的话某个参数不被覆盖的的概率会很小很小,数学的力量!!

3.3 公共类

在编写测试用例的时候,有些场景是多次重复使用的,比如发送一个图片消息等等,我们可以把构造一条图片消息封装成一个公共类,而不要傻傻的每用一次就代码编写一次生成图片的代码。随着测试用例的丰富,你的测试工程中应该有各种各样的公共类,方便你实现各种各样的测试场景。所以,随着你的测试用例越来越多,你的测试公共类也应该越来越丰富。

3.4、测试代码覆盖率

当测试代码有一定数量的时候,这时候你就得考虑一个问题,我写的测试代码到底 “好” 还是 “不好”。这里不能简单地用测试用例的个数或者测试代码的数量来衡量,因为很有可能你的测试用例有大量的重复测试,比较科学的方法是统计测试工程的代码覆盖率。关于如何统计代码覆盖率,苹果的官方文档(后文参考文献会给出)中专门有一个章节'code coverage'说明,这里就不详细讲了,各位看官自己去了解下吧:D。

四、参考文献


↙↙↙阅读原文可查看相关链接,并与作者交流