Вид контроллера TDD
Я пытаюсь добавить некоторые модульные тесты в свой проект для тестирования контроллеров представления. Однако у меня, похоже, проблемы с кажущимися простыми вещами. Я создал образец проекта, на который я буду ссылаться. https://github.com/pangers/ViewControllerTesting
образец содержит UINavigationController в качестве контроллера начального вида. Контроллер корневого представления UINavigationController является FirstViewController. На FirstViewController есть кнопка, которая переход к SecondViewController. В SecondViewController есть пустое текстовое поле.
два теста, которые я пытаюсь добавить:
1) проверьте название кнопки в FirstViewController - "следующий экран".
2) Проверьте textfield в SecondViewController пуст,"".
Я слышал отчеты о добавлении ваших файлов swift как к основной цели, так и к тестовой цели не является хорошей практикой. Но лучше сделать все, что вы хотите получить в своих тестах, общедоступным и импортируйте основную цель в тесты. Вот что я сделал. (Я также установил "определяет модуль" для основной цели да, поскольку это то, что я прочитал в нескольких статьях).
в 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?
я использую Swift с XCode 6.1.1.
спасибо заранее.
решить
хорошо после рассмотрения ответов от modocache и Mike Taverne, я нашел свое решение, и я узнал несколько вещей, которые я запишу ниже.
1) я сделал все, что класс/метод/переменная, которые я хочу проверить публично. Мне не нужно добавлять файлы swift в тестовую цель.
2) мне нужно было только установить "определяет модуль "для" основной " цели (в отличие от "тестовая" цель или весь проект)
3) при создании экземпляра раскадровки пакет должен быть установлен в nil, а не NSBundle(forClass: self.dynamicType), в противном случае тесты завершатся неудачей.
4) Как заявил modocache, хорошо дать вашему контроллеру view 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) пусть _ = файл ViewController.view действительно запускает viewDidLoad (), что было подтверждено тестом
6) пусть _ = viewController.view не запускает viewWillAppear (), и я предполагаю что-нибудь после этого. файл ViewController.viewWillAppear (false/true) необходимо вызвать вручную, чтобы вызвать его (подтверждено тестом).
надеюсь, это поможет людям. Я подтолкну обновленный проект к GitHub (ссылка выше), если кто-то захочет поиграть с ним.
обновление #2
после всего вышесказанного я все еще не мог понять, как перейти от первого контроллера вида ко второму контроллеру вида (чтобы я мог проверить свойства навигационной панели в SecondViewControllerTests.стремительный.) Я пытался!--16-->
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)
что отлично работал с моими тестами (позволил мне получить доступ к свойствам навигационной панели)!
2 ответов
Я согласен с ответом @Miketaverne'S: Я предпочитаю доступ -[UIViewController view]
для того, чтобы вызвать -[UIViewController viewDidLoad]
, а не называть его напрямую. Смотрите, если тестовые сбои для FirstViewController
уходите, как только вы используете это вместо этого:
viewController = navigationController.topViewController as FirstViewController
let _ = viewController.view
Я бы также рекомендовал предоставить идентификаторы обоих контроллеров просмотра в вашей раскадровке. Это позволит вам создавать их непосредственно, без доступа к ним через 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
}
проверьте мой разговор о тестировании UIViewController
в Бруклин Свифт для подробности:https://vimeo.com/115671189#t=37m50s (моя презентация начинается с отметки 37'50").
недавно я начал модульное тестирование контроллеров представления, и это создает некоторые уникальные проблемы.
одна задача - загрузить представление. Глядя на вашу настройку для FirstViewController, вы пытаетесь сделать это с помощью viewController.viewDidLoad ().
мое предложение-заменить эту строку следующим:
пусть dummy = viewController.вид
доступа .свойство view заставит представление загружаться. Это спровоцирует .в метод viewDidLoad ваш ViewController, поэтому не вызывайте этот метод явно в своем тесте.
этот подход считается хакерским некоторыми людьми, но он прост и эффективен. (См.чистый способ заставить view загружать подвиды рано)
в стороне, я нахожу лучший способ проверить контроллеры представления, чтобы переместить как можно больше кода из контроллеров представления в другие классы, которые легче протестировать.
Если ваш контроллер вида определен в раскадровка, то вам нужно создать его таким образом, чтобы ваши розетки были настроены правильно. Попытка инициализировать его как обычный класс не сработает.