Downcast / подкласс UIViewController для макета в модульном тесте - PullRequest
1 голос
/ 09 апреля 2019

У меня есть модульный тест, и я хочу создать версию подкласса UIViewController, например Test1ViewController. В частности, я хочу переопределить метод present для этого класса.

У меня есть расширение контроллера представления, которое создает экземпляр контроллера представления, в зависимости от его имени класса.

public class func instanceFromStoryboard<T>(storyboard: Storyboard) -> T {
    return UIStoryboard(name: storyboard.rawValue, bundle: nil).instantiateViewController(withIdentifier: String(describing: T.self)) as! T
}

И класс Раскадровки.

public enum Storyboard: String {
    case main = "Main"
}

В моем модульном тесте я создал подкласс из Test1ViewController.

class Test2ViewController: Test1ViewController {
    var presented: Bool = false
    override func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
        presented = true
    }
}

Как я могу использовать мой метод расширения, чтобы извлечь контроллер представления из раскадровки, а затем уменьшить / подкласс до Test2ViewController?

Ответы [ 3 ]

1 голос
/ 10 апреля 2019

@ Jon Reid * answer приятно подводит итог ограничения использования раскадровок.

Если вашей конечной целью является проверка того, представил ли тестируемый UIViewController что-либо, рассматривали ли вы проверку свойства presentedViewController?

// Create an asynchronous expectation to verify the view controller has presented
// something.
_ = expectation(
  for: NSPredicate(
    block: { input, _ -> Bool in
      guard let viewController = input as? UIViewController else { return false }
        // If you care about the type of the presented view controller you could
        // use `is` here to verify it
        return viewController.presentedViewController != nil
      }
    ),
    evaluatedWith: viewControllerUnderTest,
    handler: .none
)

viewControllerUnderTest.doSomething()

waitForExpectations(timeout: 1, handler: nil)

Другой подход, который требует больше работы, но также окупается с течением времени за счет упрощения перемещения контроллеров представления в навигационном потоке, заключается в использовании делегата для всех презентаций.

В этом тесте вы бы не проверяли, было ли что-то представлено, но только если был вызван метод делегата навигации для представления чего-либо, используя Spy test double . С удовольствием предоставлю более подробную информацию, если вам интересно узнать об этом подходе.

1 голос
/ 09 апреля 2019

Поскольку объекты в раскадровках фактически являются закодированными объектами, их нельзя декодировать и преобразовывать во что-либо еще.Это недостаток использования раскадровок.То, что вы кладете в раскадровку, это то, что вы получаете.

Если вы можете, используйте контроллер представления на основе XIB вместо раскадровки.С XIB (и с контроллерами с программным представлением) тесты могут создавать экземпляры подклассов.

Если нет, то вам потребуется ввести заднюю дверь в свой контроллер представления.Это было бы неудачно, так как это означает смешивание тестового кода с вашим рабочим кодом.

0 голосов
/ 10 апреля 2019

@ mokagio s ответ должно работать.Я предпочитаю другой подход.

func test_presentationOfViewController() {
  // Arrange
  let window = UIWindow(frame: CGRect(x: 0, y: 0, width: 300, height: 300))
  window.rootViewController = sut
  window.makeKeyAndVisible()

  // Act
  sut.presentNext()

  // Assert
  XCTAssertTrue(sut.presentedViewController is DetailViewController)
}
...