原标题:如何简单地为测试切换AppDelegate
本文由CocoaChina译者小袋子翻译
原文:How to Easily Switch Your App Delegate for Testing
测试驱动的开发最大好处是能够有快速反馈(译者:这是作者的另一篇文章,讲述了测试驱动的好处,有兴趣的可以看看)。所以,为了确保你的 TDD 效率,最好的方式就是尽可能快地获得反馈。
但是很多 iOS 开发者会在测试的时候使用生产环境(译者:应用开发中的不同阶段,一般分为开发环境 development,处于产品开发阶段;生产环境 production,即正式上线的环境,更详细的请参照 Development, testing, acceptance and production)的 app delegate。这是一个影响效率的问题。
你的常规 app delegate 在用于测试时是否跟龟速一样?
这是因为当你测试运行时,首先要启动你的应用——而这个过程可能做了很多事情,大量耗时的操作。而这些耗时的操作在测试的时候并不是我们所需要的。
我们应该如何避免这个问题?
测试流程Apple 习惯将单元测试归为两类:应用测试和逻辑测试。这个区别是非常重要的,因为在以前,应用测试只能在设备上运行,除非你使用完全不同的第三方测试框架。
但是这个差异现在消失了,因为 Apple 允许我们在模拟器上运行应用测试。Apple 花了很多时间来更新文档,直到在他们最新的Xcode测试才更新了这部分说明,Apple 现在称之为 "app tests" 和 "library tests"。这就使事情简化为你是开发一个应用还是一个库。并且 Xcode 为你设置了一个测试用的 target ,这正是你所需要的。
如果我现在开发一个应用(或者一个需要运行应用的库),我总是会运行应用测试,所以我停止去试图区分这两种类型的测试。但是由于 Xcode 是在一个运行的应用的上下文环境下执行应用测试,测试流程就变成这样:
启动模拟器
在模拟器中,启动应用
将测试 bundle 注入运行的应用
运行测试
那么我们怎么才能加快这个流程呢?我们可以在第二步中做文章,让应用尽可能快地启动。
普通的 app delegate在开发环境下,启动应用可能会关闭很多任务。Ole Begemann 在 Revisiting the App Launch Sequence on iOS中进行了详细的解释,但是根本上, UIApplicationMain()
最终会调用 app delegate去执行 application:didFinishLaunchingWithOptions:
。具体的流程一般取决于你的应用,但是很少会像下面这么做:
创建 Core Data。
配置根视图控制器
检测网络连通性
向服务器发送一个网络请求去取回最近的配置,例如应该在根视图中展示的东西。
因此在开始测试之前要做很多事情。难道不能更好地不被干扰,如果我们想要的只是运行我们的测试程序?
让我们来解决这个问题,下面是具体方案。
改变 main 函数让我们改变我们的 main 函数,如下所示:
#import#import"AppDelegate.h" intmain(intargc,char*argv[]) { @autoreleasepool{ returnUIApplicationMain(argc,argv,nil,NSStringFromClass([AppDelegateclass])); } }
我们现在想要去检查是否我们在运行测试代码。如果想要这么做的话,我们想要去使用一个不同的 app delegate。我们可以这么做:
最早的版本
#import#import"AppDelegate.h" #import"TestingAppDelegate.h" intmain(intargc,char*argv[]) { @autoreleasepool{ BOOLisTesting=NSClassFromString(@"XCTestCase")!=Nil; ClassappDelegateClass=isTesting?[TestingAppDelegateclass]:[AppDelegateclass]; returnUIApplicationMain(argc,argv,nil,NSStringFromClass(appDelegateClass)); } }
从根本上来说,如果 XCTestCase 链接好了,我们就会使用 TestingAppDelegate
。否则,我们退而使用生产环境的 app delegate。然后我们启动应用时可以选择我们想要的 app delegate。(注意:TestingAppDelegate 必须在生产环境的 target 中)
现在这些代码已经实现了来回切换。上述部分的实现从根本上和我原先的文章一致。因为有一段时间,根据评论中的建议,我将代码改为:
@autoreleasepool{ ClassappDelegateClass=NSClassFromString(@”XYZTestingAppDelegate”); if(appDelegateClass==nil){ appDelegateClass=[DOAAppDelegateclass]; } returnUIApplicationMain(argc,argv,nil,NSStringFromClass(appDelegateClass)); }
但是在Xcode7上不能正常运行,所以我又改回原始版本。
如果你想在单元测试外部使用 XCTest 该怎么办,例如 UI 测试?为了取代为 XCTestCase 做的测试,你可以设置一个环境变量,通过 getenv 来测试。
提供 TestingAppDelegate这里需要创建一个 TestingAppDelegate 类。正如下面代码所示:
TestingAppDelegate.h
#import@interfaceTestingAppDelegate:UIResponder@property(nonatomic,strong)UIWindow*window; @end
TestingAppDelegate.m
#import"TestingAppDelegate.h" @implementationTestingAppDelegate @end
正如你所看到的那样,不要做任何事。
(在早先的 iOS 版本中,我必须添加更多的代码,导致 TestingAppDelegate 会创建一个 window,给这个 window 设置一个不做任何事情的根视图,然后让其可见。现在看来没必要了。)
Bare bones for fast feedback
快速反馈的本质最重要的事情是我们已经从本质上减少了测试过程中启动应用的步骤。尽管还有一些不必要的开销,但是并不多。这是实现快速反馈过程中重要的一步,这样我们就可以从 TDD 中获得更多。
甚至当你开始一个新的项目,我推荐尽早使用这样的方法,因为你真正的app delegate最终会变得日益庞大。让我们在襁褓中阻止这种问题,然后保持快速的反馈。
另外一个好处是,通过完全控制哪部分该测试,什么时候测试,我们现在可以编写跟生产环境的app delegate完全不同的单元测试。这显然是双赢的。
本文中的所有译文仅用于学习和交流目的,转载请注明文章译者、出处、和本文链接。
感谢博文视点为本期翻译活动提供赞助
相关:
石工布局瀑布流(上传者:wkcao)石工布局瀑布流,用UICollectionView实现滑动打分,通过在星星上面滑动,给予
示例详解:UIScrollview与Autolayout的那点事
前言自从写了介绍Masonry那篇文章以后 就一直有人对UIScrollView的那个例子不是很理解UIView*container=[UI
HR如何快速面试APP产品经理的面试问题: 01、是否准备做背调?如果不准备的话,简历啥的就别看了,经
无论是对于公司还是开发者或设计师个人而言,面试都是一项耗时耗钱的项目,本文作者Cameron Banga从编程
作者尤原庆从雅虎到腾讯,再到华为,不仅身经百战,也面试过很多设计师,今天把这6年的面试经验总结出来
你每天在看各种产品的干货分享时,是不是经常看到一个词叫“用户体验”?是不是经常听到一些人吐槽“某某AP
圣诞购物季过去了,今年卖得好的电子产品有哪些? 根据 Qz 的消息,Fitbit 从圣诞节当天下午就爬上了榜首,
这一年的出版业和预期有所不同,推翻了些许传统观念。以下便是 2015 年文学界最令人惊奇的一些故事。 固守
先回答题干问题,日语所有的单词都可以写成汉字,但是很多日本人也看不懂。日本人的起源是哪里? – 安
2015 年快要过完了,各行业的盘点也是没少见。而当人们把目光聚焦到香港股市的时候,可能会跳出一个女士内