• 0
  • 0
分享
  • 开发人员谈测试:网络测试和UI测试——软件测试圈
  • 恬恬圈 2023-09-14 16:51:47 字数 9044 阅读 1032 收藏 0

  网络测试

  我们在测试某个方法的时候可能会遇到方法内部调用了网络通信能力:

  · 网络请求成功,可能刷新 UI 或者给出一些成功的提示

  · 网络失败或者网络不可用则给出一些失败的提示

  所以需要对网络通信去看进行模拟。

  iOS 中很多网络都是基于 NSURL 系统下的类实现的。所以我们可以利用 NSURLProtocol 的能力来监控网络并 mock 网络数据。

  开源项目 OHHTTPStubs  就是一个对网络模拟的库。它可以拦截 HTTP 请求,返回 json 数据,定制各种头信息。

  几个主要类及其功能:HTTPStubsProtocol 拦截网络请求、HTTPStubs 单例管理 HTTPStubsDescriptor 实例对象、HTTPStubsResponse 伪造 HTTP 请求。

  HTTPStubsProtocol 继承自 NSURLProtocol,可以在 HTTP 请求发送之前对 request 进行过滤处理:

+ (BOOL)canInitWithRequest:(NSURLRequest *)request
{
   BOOL found = ([HTTPStubs.sharedInstance firstStubPassingTestForRequest:request] != nil);
   if (!found && HTTPStubs.sharedInstance.onStubMissingBlock) {
      HTTPStubs.sharedInstance.onStubMissingBlock(request);
   }
   return found;
}

  firstStubPassingTestForRequest 方法内部会判断请求是否需要被当前对象处理。

  紧接着开始发送网络请求。实际上在 - (void)startLoading 方法中可以用任何网络能力去完成请求,比如NSURLSession、NSURLConnection、AFNetworking 或其他网络框架,OHHTTPStubs 的做法是获取 request、client 对象。

  如果 HTTPStubs 单例中包含 onStubActivationBlock 对象,则执行该 block,然后利用 responseBlock 对象返回一个 HTTPStubsResponse 响应对象。

  OHHTTPStubs 的具体 API 可以查看文档,文档地址:https://github.com/AliSoftware/OHHTTPStubs/wiki/Usage-Examples。

  举个例子,利用 Kiwi、OHHTTPStubs 测试离线包功能,代码如下:

@interface HORouterManager (Unittest)
- (void)fetchOfflineInfoIfNeeded;
@end
SPEC_BEGIN(HORouterTests)
describe(@"routerTests", ^{
    context(@"criticalPath", ^{
        __block HORouterManager *routerManager = nil;
        beforeAll(^{
            routerManager = [[HORouterManager alloc] init];
        });
        it(@"getLocalPath", ^{
            __block NSString *pagePath = nil;
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                pagePath = [routerManager filePathOfUrl:@"http://***/resource1"];
            });
            [[expectFutureValue(pagePath) shouldEventuallyBeforeTimingOutAfter(5)] beNonNil];
            
            __block NSString *rescPath = nil;
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                rescPath = [routerManager filePathOfUrl:@"http://***/resource1"];
            });
            [[expectFutureValue(rescPath) shouldEventuallyBeforeTimingOutAfter(5)] beNonNil];
        });
        it(@"fetchOffline", ^{
            [HOOfflineManager sharedInstance].offlineInfoInterval = 0;
            [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) {
                return [request.URL.absoluteString containsString:@"h5-offline-pkg"];
            } withStubResponse:^OHHTTPStubsResponse*(NSURLRequest *request) {
                NSMutableDictionary *dict = [NSMutableDictionary dictionary];
                dict[@"code"] = @(0);
                dict[@"data"] = @"f722fc3efce547897819e9449d2ac562cee9075adda79ed74829f9d948e2f6d542a92e969e39dfbbd70aa2a7240d6fa3e51156c067e8685402727b6c13328092ecc0cbc773d95f9e0603b551e9447211b0e3e72648603e3d18e529b128470fa86aeb45d16af967d1a21b3e04361cfc767b7811aec6f19c274d388ddae4c8c68e857c14122a44c92a455051ae001fa7f2b177704bdebf8a2e3277faf0053460e0ecf178549e034a086470fa3bf287abbdd0f79867741293860b8a29590d2c2bb72b749402fb53dfcac95a7744ad21fe7b9e188881d1c24047d58c9fa46b3ebf4bc42a1defc50748758b5624c6c439c182fe21d4190920197628210160cf279187444bd1cb8707362cc4c3ab7486051af088d7851846bea21b64d4a5c73bd69aafc4bb34eb0862d1525c4f9a62ce64308289e2ecbc19ea105aa2bf99af6dd5a3ff653bbe7893adbec37b44a088b0b74b80532c720c79b7bb59fda3daf85b34ef35";
                NSData *data = [NSJSONSerialization dataWithJSONObject:dict options:0 error:nil];
                return [OHHTTPStubsResponse responseWithData:data
                                                  statusCode:200
                                                     headers:@{@"Content-Type":@"application/json"}];
            }];
            [routerManager fetchOfflineInfoIfNeeded];
            [[HOOfflineInfo shouldEventually] receive:@selector(saveToLocal:)];
        });
    });
});
SPEC_END

  插一嘴,我贴的代码已经好几次可以看到不同的测试框架组合了,所以不是说选了框架 A 就完事,根据场景选择最优解。

  UI 测试

  上面文章大篇幅的讲了单元测试相关的话题,单元测试十分适合代码质量、逻辑、网络等内容的测试,但是针对最终产物 App 来说单元测试就不太适合了,如果测试 UI 界面的正确性、功能是否正确显然就不太适合了。

  Apple 在 Xcode 7 开始推出的 UI Testing  就是苹果自己的 UI 测试框架。

  很多 UI 自动化测试框架的底层实现都依赖于 Accessibility,也就是 App 可用性,UI Accessibility 是 iOS 3.0 引入的一个人性化功能,帮助身体不便的人士方便使用 App。

  Accessibility 通过对 UI 元素进行分类和标记。分类成类似按钮、文本框、文本等类型,使用 identifier 来区分不同 UI 元素,无痕埋点的设计与实现里面也使用 accessibilityIdentifier 来绑定业务数据。

  1、使用 Xcode 自带的 UI测试则在创建工程的时候需要勾选 “Include UI Tests”。

  2、像单元测试意义,UI 测试方法命名以 test 开头。将鼠标光标移到方法内,点击 Xcode 左下方的红色按钮,开始录制 UI 脚本。

2-1.png

  解释说明:

/*! Proxy for an application that may or may not be running. */
@interface XCUIApplication : XCUIElement
// ...
@end

  XCUIApplication launch 来启动测试,XCUIApplication 是 UIApplication 在测试进程中的代理,用来和 App 进行一些交互。

  使用 staticTexts来获取当前屏幕上的静态文本(UILabel)元素的代理。等价于 [app descendantsMatchingType:XCUIElementTypeStaticText]。XCUIElementTypeStaticText 参数是枚举类型。

typedef NS_ENUM(NSUInteger, XCUIElementType) {
    XCUIElementTypeAny = 0,
    XCUIElementTypeOther = 1,
    XCUIElementTypeApplication = 2,
    XCUIElementTypeGroup = 3,
    XCUIElementTypeWindow = 4,
    XCUIElementTypeSheet = 5,
    XCUIElementTypeDrawer = 6,
    XCUIElementTypeAlert = 7,
    XCUIElementTypeDialog = 8,
    XCUIElementTypeButton = 9,
    XCUIElementTypeRadioButton = 10,
    XCUIElementTypeRadioGroup = 11,
    XCUIElementTypeCheckBox = 12,
    XCUIElementTypeDisclosureTriangle = 13,
    XCUIElementTypePopUpButton = 14,
    XCUIElementTypeComboBox = 15,
    XCUIElementTypeMenuButton = 16,
    XCUIElementTypeToolbarButton = 17,
    XCUIElementTypePopover = 18,
    XCUIElementTypeKeyboard = 19,
    XCUIElementTypeKey = 20,
    XCUIElementTypeNavigationBar = 21,
    XCUIElementTypeTabBar = 22,
    XCUIElementTypeTabGroup = 23,
    XCUIElementTypeToolbar = 24,
    XCUIElementTypeStatusBar = 25,
    XCUIElementTypeTable = 26,
    XCUIElementTypeTableRow = 27,
    XCUIElementTypeTableColumn = 28,
    XCUIElementTypeOutline = 29,
    XCUIElementTypeOutlineRow = 30,
    XCUIElementTypeBrowser = 31,
    XCUIElementTypeCollectionView = 32,
    XCUIElementTypeSlider = 33,
    XCUIElementTypePageIndicator = 34,
    XCUIElementTypeProgressIndicator = 35,
    XCUIElementTypeActivityIndicator = 36,
    XCUIElementTypeSegmentedControl = 37,
    XCUIElementTypePicker = 38,
    XCUIElementTypePickerWheel = 39,
    XCUIElementTypeSwitch = 40,
    XCUIElementTypeToggle = 41,
    XCUIElementTypeLink = 42,
    XCUIElementTypeImage = 43,
    XCUIElementTypeIcon = 44,
    XCUIElementTypeSearchField = 45,
    XCUIElementTypeScrollView = 46,
    XCUIElementTypeScrollBar = 47,
    XCUIElementTypeStaticText = 48,
    XCUIElementTypeTextField = 49,
    XCUIElementTypeSecureTextField = 50,
    XCUIElementTypeDatePicker = 51,
    XCUIElementTypeTextView = 52,
    XCUIElementTypeMenu = 53,
    XCUIElementTypeMenuItem = 54,
    XCUIElementTypeMenuBar = 55,
    XCUIElementTypeMenuBarItem = 56,
    XCUIElementTypeMap = 57,
    XCUIElementTypeWebView = 58,
    XCUIElementTypeIncrementArrow = 59,
    XCUIElementTypeDecrementArrow = 60,
    XCUIElementTypeTimeline = 61,
    XCUIElementTypeRatingIndicator = 62,
    XCUIElementTypeValueIndicator = 63,
    XCUIElementTypeSplitGroup = 64,
    XCUIElementTypeSplitter = 65,
    XCUIElementTypeRelevanceIndicator = 66,
    XCUIElementTypeColorWell = 67,
    XCUIElementTypeHelpTag = 68,
    XCUIElementTypeMatte = 69,
    XCUIElementTypeDockItem = 70,
    XCUIElementTypeRuler = 71,
    XCUIElementTypeRulerMarker = 72,
    XCUIElementTypeGrid = 73,
    XCUIElementTypeLevelIndicator = 74,
    XCUIElementTypeCell = 75,
    XCUIElementTypeLayoutArea = 76,
    XCUIElementTypeLayoutItem = 77,
    XCUIElementTypeHandle = 78,
    XCUIElementTypeStepper = 79,
    XCUIElementTypeTab = 80,
    XCUIElementTypeTouchBar = 81,
    XCUIElementTypeStatusItem = 82,
};

  通过 XCUIApplication 实例化对象调用 descendantsMatchingType: 方法得到的是 XCUIElementQuery 类型。比如 @property (readonly, copy*) XCUIElementQuery *staticTexts;:

/*! Returns a query for all descendants of the element matching the specified type. */
- (XCUIElementQuery *)descendantsMatchingType:(XCUIElementType)type;

  descendantsMatchingType 返回所有后代的类型匹配对象。childrenMatchingType 返回当前层级子元素的类型匹配对象:

/*! Returns a query for direct children of the element matching the specified type. */
- (XCUIElementQuery *)childrenMatchingType:(XCUIElementType)type;

  拿到 XCUIElementQuery 后不能直接拿到 XCUIElement。和 XCUIApplication 类似,XCUIElement 不能直接访问 UI 元素,它是 UI 元素在测试框架中的代理。可以通过 Accessibility 中的 frame、identifier 来获取。

  对比很多自动化测试框架都需要找出 UI 元素,也就是借助于 Accessibility 的 identifier。

  第三方 UI 自动化测试框架挺多的,可以查看下典型的 appium、macaca。

  测试经验总结

  TDD 写好测试再写业务代码,BDD 先写实现代码,再写基于行为的测试代码。

  另一种思路是没必要针对每个类的私有方法或者每个方法进行测试,因为等全部功能做完后针对每个类的接口测试,一般会覆盖据大多数的方法。等测试完看如果方法未被覆盖,则针对性的补充 Unit Test。

  目前,UI 测试(appium) 还是建议在核心逻辑且长时间没有改动的情况下去做,这样子每次发版本的时候可以当作核心逻辑回归了,目前来看价值是方便后续的迭代和维护上有一些便利性,其他的功能性测试还是走 BDD。

  对于类、函数、方法的走 TDD,老老实实写 UT、走 UT 覆盖率的把控。

  UITesting 还是建议在核心逻辑且长时间没有改动的情况下去做,这样子每次发版本的时候可以当作核心逻辑回归,目前来看价值是方便后续的迭代和维护上有一些便利性。例如用户中心 SDK 升级后,当时有了UITesing,基本上免去了测试人员介入。

  如果是一些活动页和逻辑经常变动的,老老实实走测试黑盒……

  我觉得一直有个误区,就是觉得自动测试是为了质量,其实质量都是附送的,测试先行是让开发更快更爽的。

2-2.png

  WWDC 这张图也很清楚,UI 其实需要的占比较小,还是要靠单测驱动。


作者:杭城小刘    

来源:http://www.51testing.com/html/03/n-4481203.html

  • 【留下美好印记】
    赞赏支持
登录 后发表评论
+ 关注

热门文章

    最新讲堂

      • 推荐阅读
      • 换一换
          • 基本技术问题:游戏测试的基本流程:分析测试需求-制定测试计划-设计测试用例-(可能会进行冒烟测试)-执行测试-生成测试报告OSI七层协议:应用层-表示层-会话层-传输层-网络层-数据链路层-物理层TCP与UDP的差别:游戏测试与软件测试之间的关系:1、游戏测试是软件测试的一部分游戏测试简单说就是发现游戏里的问题(BUG)并进行改进,从而提升游戏产品的质量。游戏测试作为软件测试的一部分,它具备了软件测试所有的一切共同的特性:①测试的目的是发现软件中存在的缺陷。②测试都是需要测试人员按照产品行为描述来实施。产品行为描述可以是书面的规格说明书,需求文档,产品文件,或是用户手册,源代码,或是工作的可执...
            3 3 1883
            分享
          •  背景介绍:  介绍搜狗输入法按键响应自动化测试的方法及工具,虽然本文中的工具是深度定制化的输入法测试工具,无法应用在其他项目,但相信本文中所使用的Xposed Hook技术、socket多进程通信技术以及自动化思想会对大家有帮助。  按键响应测试是什么?  当我们在手机上打一个字时,它大致经历了如下过程:  因此,在以上过程中,从用户按下软键盘上的按键到候选词显示的所花费的时间,是衡量输入法性能的一项重要指标。  按键响应怎么测试?  按键响应的测试方法有两种:日志方式和视频拍摄方式。  日志方式:在上述用户按下按键时,程序执行onKeyEvent函数中写日志记下时间戳T1;当开始显示候选词...
            0 0 2134
            分享
          • 大家都知道,测试Android系统原生态APP目前最好的工具使UiAutomation,随着DevOps的普及,我们需要尽早地发现程序中的缺陷,所以单元测试变得非常重要,Android系统推出了Espresso测试框架。Espresso与UiAutomation最显著区别在于UiAutomation可以测试一个APP多个界面(Active),而Espresso只能测试一个APP一个界面(Active),另外UiAutomation可以独立测试APP建立专门的Project,Espresso必须建立在待测APP同一个目录下。下面我向大家简单介绍一下Espresso。进行Espresso,首先在待...
            0 1 2828
            分享
          •   前言  功能测试的天花板在15k左右,自动化的入行在15k左右。这两个需要掌握的技能完全不一样,先聊薪资吧。  如果刚入门学习结束后,保守点说薪资在7.5k。这个薪资不高的原因主要是之前没有相关的IT行业工作经验。一线城市的功能测试月薪大概在8k左右。  二三线城市基本就是5k左右或者以上,虽然现在测试的薪资水平也有不同的提升,但是只会功能测试没有任何优势。不会自动化、性能、接口在就业上就没有什么竞争力。慢慢给大家从0到1的介绍测必会的知识。  作为一个过来人来讲,如果大家想进阶,一定要系统的学习。  我按照薪资的不同大致划分成2个档位:  ·月薪5-9k:从零基础入门到能够找到工作  ·...
            0 0 795
            分享
          • 当HR压你价,说你只值7K时,你可以流畅地回答,记住,是流畅,不能犹豫。礼貌地说:“7K是吗?了解了。嗯~其实我对贵司的面试官印象很好。只不过,现在我的手头上已经有一份11K的offer。来面试,主要也是自己对贵司挺有兴趣的,所以过来看看...”(未完)这段话主要是陪HR互诈的同时,从公司兴趣,公司职员印象上,都给予对方正面的肯定,既能提升HR的好感度,又能让谈判气氛融洽,为后面的发挥留足空间。委婉地说出“你已拿到一份11K的offer”这样的话,是为了拉回谈判的话语权,平衡掉HR说你只值7K的贬值语境。(ps: 这里的说辞可灵活变通,关键是备好一个让自己保值的理由)这种“绵里藏针”的谈判方式...
            1 5 4244
            分享
      • 51testing软件测试圈微信