视图控制器TDD

我正在尝试添加一些unit testing到我的项目来testing视图控制器。 但是,我似乎有看似简单的事情有问题。 我已经创build了一个我将参考的示例项目。 https://github.com/pangers/ViewControllerTesting

该示例包含一个UINavigationController作为初始视图控制器。 UINavigationController的根视图控制器是FirstViewController。 FirstViewController上有一个button,可以连接到SecondViewController。 在SecondViewController中有一个空的文本字段。

我试图添加的两个testing是:
1)检查FirstViewController中的button标题是“下一个屏幕”。
2)检查SecondViewController中的文本字段为空,“”。

我听说有关将swift文件添加到主目标和testing目标的报告不是很好的做法。 而是在你的公共testing中做任何你想要访问的东西,并把主要目标导入到testing中。 这就是我所做的。 (我也把主要目标的“定义模块”设置为YES,就像我在几篇文章中所读到的那样)。

在FirstViewControllerTests我已经实例化第一个视图控制器与以下内容:

var viewController: FirstViewController! override func setUp() { let storyboard = UIStoryboard(name: "Main", bundle: NSBundle(forClass: self.dynamicType)) let navigationController = storyboard.instantiateInitialViewController() as UINavigationController viewController = navigationController.topViewController as FirstViewController viewController.viewDidLoad() } 

我已经添加了testing:

 func testCheckButtonHasTextNextScreen() { XCTAssertEqual(viewController.button.currentTitle!, "Next Screen", "Button should say Next Screen") } 

同样,对于SecondViewControllerTest,我已经使用:

 var secondViewController:SecondViewController! override func setUp() { let storyboard = UIStoryboard(name: "Main", bundle: NSBundle(forClass: self.dynamicType)) let navigationController = storyboard.instantiateInitialViewController() as UINavigationController let firstviewController = navigationController.topViewController as FirstViewController firstviewController.performSegueWithIdentifier("FirstToSecond", sender: nil) secondViewController = navigationController.topViewController as SecondViewController secondViewController.viewDidLoad() } 

和testing:

 func testTextFieldIsBlank() { XCTAssertEqual(secondViewController.textField.text, "", "Nothing in textfield") } 

他们都失败了,我不太确定为什么。 我的怀疑是,我实例化视图控制器的方式是不正确的。 实例化视图控制器的最好方法是使用故事板(就像在现实生活中运行一样)? 或者可以通过以下方式实例化:

 var viewController = FirstViewController() 

你们在TDD和视图控制器方面有什么经验?

我正在使用Xcode 6.1.1的Swift。

提前致谢。

解决了

好,考虑到modocache和Mike Taverne的答案后,我find了解决办法,并且学到了一些我将在下面写下来的东西。

1)我做任何我想testing公共的类/方法/variables。 我不需要将swift文件添加到testing目标。

2)我只需要为“主”目标(而不是“testing”目标或整个项目)设置“定义模块”

3)在实例化故事板时,应该将bundle设置为nil而不是NSBundle(forClass:self.dynamicType),否则testing将失败。

4)正如modocache所说,给你的视图控制器的一个StoryboardID和实例化它们是这样的:

 viewController = storyboard.instantiateViewControllerWithIdentifier("FirstViewController") as FirstViewController 

但是,像这样实例化视图控制器只能单独实例化视图控制器,而不是任何可能embedded的导航控制器。也就是说,试图做

 XCTAssertFalse(viewController.navigationController!.navigationBarHidden, "Bar should show by default") 

将导致一个零例外。 我确认了这一点

 XCTAssertNil(viewController.navigationController?, "navigation controller doesn't exist") 

这导致了一个成功的testing。

因为我想检查FirstViewController中导航栏的状态,所以你必须像这样实例化视图控制器:

 let storyboard = UIStoryboard(name: "Main", bundle: nil) let navigationController = storyboard.instantiateInitialViewController() as UINavigationController viewController = navigationController.topViewController as FirstViewController 

现在执行testing

 XCTAssertFalse(viewController.navigationController!.navigationBarHidden, "nav bar should be showing by default") 

导致一个成功的testing。

5)让_ = viewController.view确实会触发viewDidLoad(),这是由testing确认

6)让_ = viewController.view不会触发viewWillAppear(),我以后也会设定任何东西。 需要手动调用viewController.viewWillAppear(false / true)来触发它(由testing确认)。

希望这会对人们有所帮助。 如果有人想玩,我会把更新的项目推到GitHub(上面的链接)。

更新#2

毕竟以上,我仍然不知道如何从第一个视图控制器转换到第二个视图控制器(以便我可以在SecondViewControllerTests.swift中testing导航栏属性)。 我试过了

 let storyboard = UIStoryboard(name: "Main", bundle: nil) let nc = storyboard.instantiateInitialViewController() as UINavigationController let firstVC = nc.topViewController as FirstViewController firstVC.performSegueWithIdentifier("FirstToSecond", sender: nil) secondVC = nc.topViewController as SecondViewController 

这导致了一个错误。

我也试过了

 let storyboard = UIStoryboard(name: "Main", bundle: nil) let nc = storyboard.instantiateInitialViewController() as UINavigationController let firstVC = nc.topViewController as FirstViewController firstVC.toSecondVCButton.sendActionsForControlEvents(UIControlEvents.TouchUpInside) secondVC = nc.topViewController as SecondViewController 

哪个没用

我最终尝试了

 let storyboard = UIStoryboard(name: "Main", bundle: nil) let nc = storyboard.instantiateInitialViewController() as UINavigationController vc = storyboard.instantiateViewControllerWithIdentifier("Second") as SecondViewController nc.pushViewController(vc, animated: false) let _ = vc.view vc.viewWillAppear(false) 

这完全符合我的testing(允许我访问导航栏属性)!

我同意@ MikeTaverne的回答:我更喜欢访问-[UIViewController view]以触​​发-[UIViewController viewDidLoad] ,而不是直接调用它。 查看FirstViewController的testing失败是否FirstViewController ,一旦你使用它,而不是:

 viewController = navigationController.topViewController as FirstViewController let _ = viewController.view 

我还build议在故事板中同时提供两个视图控制器标识符。 这将允许您直接实例化它们,而无需通过UINavigationController访问它们:

 var secondViewController: SecondViewController! override func setUp() { let storyboard = UIStoryboard(name: "Main", bundle: NSBundle(forClass: self.dynamicType)) secondViewController = storyboard.instantiateViewControllerWithIdentifier("SecondViewController") as SecondViewController let _ = secondViewController.view } 

看看我在布鲁克林斯威夫特testingUIViewController谈话: https ://vimeo.com/115671189#t = 37m50s(我的演讲开始于37'50“标记)。

我最近已经开始对视图控制器进行unit testing,并且带来了一些独特的挑战。

一个挑战是加载视图。 看着你的FirstViewController的设置,你正在试图用viewController.viewDidLoad()来做到这一点。

我的build议是用这个replace这一行:

让dummy = viewController.view

访问.view属性将强制视图加载。 这将触发ViewController中的.viewDidLoad,因此不要在testing中显式调用该方法。

这种方法被一些人认为是不好的,但是简单而有效。 (请参阅清理方式以强制视图加载子视图 )

另一方面,我发现testing视图控制器的最好方法是将尽可能多的代码从视图控制器中移出到其他更容易testing的类中。

如果您的视图控制器是在故事板中定义的,那么您需要将其实例化,以便您的sockets正确设置。 试图像普通的类初始化它将无法正常工作。