View Controller TDD

Estou tentando adicionar alguns testes de unidade ao meu projeto para testar os controladores de exibição. No entanto, parece que estou tendo problemas com coisas aparentemente simples. Eu criei um projeto de amostra ao qual me referirei.https: //github.com/pangers/ViewControllerTestin

O exemplo contém um UINavigationController como o controlador de exibição inicial. O controlador de exibição raiz do UINavigationController é FirstViewController. Há um botão no FirstViewController que segue para SecondViewController. No SecondViewController, há um campo de texto vazio.

Os dois testes que estou tentando adicionar são:
1) Verifique o título do botão no FirstViewController como "Próxima tela"
2) Verifique se o campo de texto no SecondViewController está vazio, "".

Ouvi relatos de adição de arquivos rápidos ao alvo principal e ao alvo de teste não é uma boa prática. Mas é melhor tornar público o que você deseja acessar nos testes e importar o destino principal para os testes. Então foi isso que eu fiz. (Também configurei o "Define Module" para o destino principal como YES, pois foi o que li em alguns artigos).

In FirstViewControllerTests Instanciei o primeiro controlador de exibição com o seguinte:

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()
}

E eu adicionei o teste:

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

Da mesma forma, para SecondViewControllerTest, eu o configurei usando:

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()
}

E o teste:

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

Ambos falham e não tenho muita certeza do porquê. Minha suspeita é que a maneira como estou instanciando os controladores de exibição não está correta. A melhor maneira de instanciar os controladores de exibição é usar o storyboard (como faria se fosse executado na vida real)? Ou é aceitável ser instanciado via:

var viewController = FirstViewController()

Qual é a experiência de vocês com o TDD e visualizar os controladores rapidamente?

Estou usando o Swift com o XCode 6.1.

Desde já, obrigado

Resolvido

Ok, depois de considerar as respostas de modocache e Mike Taverne, encontrei minha solução e aprendi algumas coisas que irei escrever abaix

1) Criei qualquer classe / método / variável que eu queira testar em público. Não preciso adicionar os arquivos rápidos ao destino de test

2) Eu só precisava definir "Define Module" para o destino "Main" (em oposição ao destino "Test" ou a todo o projeto)

3) Ao instanciar o storyboard, o pacote configurável deve ser nulo em vez de NSBundle (forClass: self.dynamicType); caso contrário, os testes falharã

4) Como o modocache afirmou, é bom fornecer um StoryboardID ao seu controlador de exibição e instancia-lo da seguinte maneira:

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

No entanto, instanciar o controlador de exibição como este SOMENTE instancia o controlador de exibição sozinho, e não quaisquer controladores de navegação nos quais ele possa estar incorporado. Isso significa, tentar executar

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

resultará em uma exceção nula. Confirmei isso com

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

que resultou em um teste bem-sucedido.

Desde que eu queria verificar o estado da barra de navegação no FirstViewController, você deve instanciar o controlador de exibição da seguinte forma:

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

Agora realizando o teste

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

esultados em um teste bem-sucedid

5) deixe _ = viewController.view de fato acionar viewDidLoad () que foi confirmado por um teste

6) deixe _ = viewController.view não acionar viewWillAppear (), e eu presumo qualquer coisa depois. O viewController.viewWillAppear (false / true) precisa ser chamado manualmente para acioná-lo (confirmado por um teste).

Espero que isso ajude as pessoas. Vou enviar o projeto atualizado para o GitHub (link acima), se alguém quiser brincar com el

Update # 2

Depois de tudo o que foi dito acima, ainda não consegui descobrir como fazer a transição do primeiro controlador de exibição para o segundo controlador de exibição (para testar as propriedades da barra de navegação no SecondViewControllerTests.swift). Eu tente

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

que causou um erro.

Eu também tentei

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

que não funcionou.

Eu tentei

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)

que funcionou perfeitamente com meus testes (me permitiu acessar as propriedades da barra de navegação)!

questionAnswers(2)

yourAnswerToTheQuestion