View Controller TDD
Я пытаюсь добавить в свой проект несколько юнит-тестов для проверки контроллеров представления. Однако, похоже, у меня проблемы с, казалось бы, простыми вещами. Я создал пример проекта, на который я буду ссылаться.https: //github.com/pangers/ViewControllerTestin
Образец содержит UINavigationController в качестве начального контроллера представления. Корневым контроллером представления UINavigationController является FirstViewController. На FirstViewController есть кнопка, которая переключается на SecondViewController. В SecondViewController есть пустое текстовое поле.
Два теста, которые я пытаюсь добавить:
1) Проверьте заголовок кнопки в FirstViewController «Следующий экран».
2) Проверьте, что текстовое поле в SecondViewController пустое, "".
Я слышал сообщения о добавлении ваших быстрых файлов как к основной цели, так и к контрольной цели, что не является хорошей практикой. Но лучше сделать общедоступным все, что вы хотите, в своих тестах и импортировать основную цель в тесты. Вот что я сделал. (Я также установил «Определить модуль» для основной цели на 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()
}
И я добавил тест:
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()
}
И тест:
func testTextFieldIsBlank() {
XCTAssertEqual(secondViewController.textField.text, "", "Nothing in textfield")
}
Они оба терпят неудачу, и я не уверен, почему. Я подозреваю, что способ, которым я создаю экземпляры контроллеров представления, не является правильным. Является ли лучший способ создания контроллеров представления, это использовать раскадровку (как если бы она работала в реальной жизни)? Или это приемлемо для реализации через:
var viewController = FirstViewController()
Какой у вас, ребята, опыт работы с TDD и быстрого просмотра контроллеров?
Я использую Swift с XCode 6.1.1.
Заранее спасибо
Решаемые
Хорошо, после рассмотрения ответов от modocache и Майка Таверна, я нашел свое решение и узнал несколько вещей, которые я напишу ниже.
1) Я сделал что-нибудь класс / метод / переменную, которую я хочу протестировать публично. Мне не нужно добавлять файлы swift к цели теста.
2) Мне нужно было только установить «Defines Module» для цели «Main» (в отличие от цели «Test» или всего проекта)
3) При создании сценария раскадровки следует установить значение nil, а не NSBundle (forClass: self.dynamicType), иначе тесты не пройдут.
4) Как было сказано в modocache, хорошо бы дать контроллеру представления StoryboardID и создать его экземпляры следующим образом:
viewController = storyboard.instantiateViewControllerWithIdentifier("FirstViewController") as FirstViewController
Однако создание такого экземпляра контроллера представления ТОЛЬКО создает экземпляр только контроллера представления, а не каких-либо контроллеров навигации, в которые он может быть встроен. Это означает, что попытка сделать
XCTAssertFalse(viewController.navigationController!.navigationBarHidden, "Bar should show by default")
приведет к нулевому исключению. Я подтвердил это с
XCTAssertNil(viewController.navigationController?, "navigation controller doesn't exist")
что привело к успешному тестированию.
Так как я хотел проверить состояние панели навигации в FirstViewController, вы должны создать экземпляр контроллера вида следующим образом:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let navigationController = storyboard.instantiateInitialViewController() as UINavigationController
viewController = navigationController.topViewController as FirstViewController
Сейчас выполняю тест
XCTAssertFalse(viewController.navigationController!.navigationBarHidden, "nav bar should be showing by default")
результаты успешного теста.
5) let _ = viewController.view действительно вызывает viewDidLoad (), что было подтверждено тестом
6) let _ = viewController.view не вызывает viewWillAppear (), и я предполагаю, что что-нибудь потом тоже будет хорошо. viewController.viewWillAppear (false / true) необходимо вызвать вручную, чтобы вызвать его (подтверждено тестом).
Надеюсь, это поможет людям. Я отправлю обновленный проект на GitHub (ссылка выше), если кто-то захочет поиграть с ним.
Обновление # 2
После всего вышесказанного я все еще не мог понять, как перейти от первого контроллера представления ко второму контроллеру представления (чтобы я мог проверить свойства панели навигации в SecondViewControllerTests.swift). Я пыталс
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)
который отлично работал с моими тестами (позволил мне получить доступ к свойствам панели навигации)!