我试图在我的iOS项目中加入UI测试,但是有一件事情继续让我感到震惊的是,你所编写的所有测试似乎必须从应用程序的开头开始并运行它们的工作。通过。例如,如果我想测试登录屏幕后面的视图,我的测试必须首先在登录屏幕上运行,输入用户名/密码,单击登录,然后转到我想要测试的视图。理想情况下,登录视图和下一个视图的测试将完全隔离。有没有办法做到这一点,还是我完全错过了UI测试背后的哲学?
答案 0 :(得分:24)
绝对!
您需要的是一个干净的应用程序环境,您可以在其中运行测试 - 一个空白的平板。
所有应用程序都有一个应用程序委托,它设置应用程序的初始状态,并在启动时提供根视图控制器。出于测试目的,您不希望发生这种情况 - 您需要能够独立测试,而不会发生所有这些事情。理想情况下,您希望能够拥有屏幕,只加载该屏幕,并且不会发生其他状态变化。
为此,您可以创建一个对象,仅用于实现UIApplicationDelegate
的测试。您可以告诉应用程序以“测试模式”运行,并使用启动参数使用特定于测试的应用程序委托。
目标-C: 的main.m:
int main(int argc, char * argv[]) {
NSString * const kUITestingLaunchArgument = @"org.quellish.UITestingEnabled";
@autoreleasepool {
if ([[NSUserDefaults standardUserDefaults] valueForKey:kUITestingLaunchArgument] != nil){
return UIApplicationMain(argc, argv, nil, NSStringFromClass([TestingApplicationDelegate class]));
} else {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([ProductionApplicationDelegate class]));
}
}
}
夫特: main.swift:
let kUITestingLaunchArgument = "org.quellish.UITestingEnabled"
if (NSUserDefaults.standardUserDefaults().valueForKey(kUITestingLaunchArgument) != nil){
UIApplicationMain(Process.argc, Process.unsafeArgv, NSStringFromClass(UIApplication), NSStringFromClass(TestingApplicationDelegate))
} else {
UIApplicationMain(Process.argc, Process.unsafeArgv, NSStringFromClass(UIApplication), NSStringFromClass(AppDelegate))
}
您必须从Swift类中删除任何@UIApplicationMain
注释。
对于“应用程序测试”,请务必在Xcode中设置方案的“测试”操作以提供启动参数:
对于UI测试,您可以将启动参数设置为测试的一部分:
目标-C:
XCUIApplication *app = [[XCUIApplication alloc] init];
[app setLaunchArguments:@[@"org.quellish.UITestingEnabled"] ];
[app launch];
夫特:
let app = XCUIApplication()
app.launchArguments = [ "org.quellish.UITestingEnabled" ]
app.launch()
这允许测试特定地使用应用程序委托进行测试。这使您有了很多控制权 - 现在您可以使用空白板进行测试。测试应用程序委托可以加载特定的故事板或放置一个空的UIViewController
。作为UI测试的一部分,您可以实例化测试中的视图控制器并将其设置为keyWindow
的根视图控制器或以模态方式呈现它。一旦添加或呈现,您的测试就可以执行,并在完成时删除或解除它。
答案 1 :(得分:7)
如果您不介意原始的UI加载,只需跳转到目标UI:
override func setUp() {
super.setUp()
continueAfterFailure = false
XCUIApplication().launch()
let storyboard = UIStoryboard(name: "MainStoryboard", bundle: NSBundle.mainBundle())
let controller = storyboard.instantiateViewControllerWithIdentifier("LanguageSelectController")
UIApplication.sharedApplication().keyWindow?.rootViewController = controller
}
如果你不想加载下面的原始UI,那么也要从你的测试中传入:
app.launchArguments.append("skipEntryViewController")
然后在didFinishLaunchingWithOptions
中,您可以查看:
if NSProcessInfo.processInfo().arguments.contains("skipEntryViewController") {
// then do NOT call makeKeyAndVisible
}
答案 2 :(得分:-3)
不幸的是,通过UI测试,您所描述的场景是不可能的。
我采取的一种方法是将我的测试分组为" flow"的功能。例如,让我们说我想测试功能A,功能B和功能C.我需要登录才能使这三个功能正常工作。
对于每个测试,我都没有启动应用程序,登录,然后最终运行实际测试。相反,我启动应用程序并登录一次。然后,我将测试分为三个私有帮助方法,testFeatureA()
,testFeatureB()
和testFeatureC()
。
通过创建单个流,测试套件将花费更短的时间来运行。最大的缺点是如果功能A失败,那么功能B将永远不会被测试。只有当您关心所有的测试通过时,才应使用此方法。
使用XCTest helper且__LINE__
和__FILE__
参数默认值的加分点。然后,您可以将这些内容传递到XCTFail()
来电,以便在testFeatureA()
上显示失败行。