Мне нужно написать модульный тест для следующего метода
func setLabelText(msg: String) {
DispatchQueue.main.async {
self.label.text = msg
}
}
Мне нужно написать модульный тест для следующего метода
func setLabelText(msg: String) {
DispatchQueue.main.async {
self.label.text = msg
}
}
Предположим, что ваша тестовая установка уже создала контроллер представления и вызывает loadViewIfNeeded()
для подключения любых розеток. (Это из главы 5, Загрузка контроллеров представления.) И что этот контроллер представления находится в свойстве, которое я назову sut
(что означает «Тестируемая система»).
Если вы напишите тестовый пример для вызова setLabelText(msg:)
, то сразу же проверьте метку контроллера представления, это не сработает.
Если бы у вас была отправка в фоновый поток, нам нужно было бы, чтобы тест дождался завершения потока. Но это не относится к этому коду.
Ваш производственный код вызывает setLabelText(msg:)
в фоновом режиме. Но тестовый код выполняется в основном потоке. Поскольку он уже находится в основном потоке, все, что нам нужно сделать, это еще раз выполнить цикл выполнения. Вы можете выразить это с помощью вспомогательной функции, которую я представил в главе 10 «Тестирование навигации между экранами»:
func executeRunLoop() {
RunLoop.current.run(until: Date())
}
При этом вот тест, который работает:
func test_setLabelText_thenExecutingMainRunLoop_shouldUpdateLabel() throws {
sut.setLabelText(msg: "TEST")
executeRunLoop()
XCTAssertEqual(sut.label.text, "TEST")
}
Это успешно проверяет метод и быстро завершается. Но что, если появится другой программист и изменит setLabelText(msg:)
, вытащив вызов self.label.text = msg
за пределы DispatchQueue.main.async
? Я описываю эту проблему в главе 13 «Тестирование сетевых ответов (и замыканий)» в разделе «Сохраняйте асинхронный код в его замыкании». По сути, мы хотим проверить, что метка не меняется, когда отправленное закрытие не выполняется. Мы можем сделать это с помощью второго теста:
func test_setLabelText_withoutExecutingMainRunLoop_shouldNotUpdateLabel() throws {
sut.label.text = "123"
sut.setLabelText(msg: "TEST")
XCTAssertEqual(sut.label.text, "123")
}
Здравствуйте, на мой взгляд, использование XCTestExpectation
могло бы принести пользу, поскольку этот API был предназначен для тестирования асинхронных операций (подробнее об этом можно прочитать здесь)
Что касается сравнения между ними, единственное, о чем я мог подумать, это то, что с XCTestExpectation
вы сможете протестировать тайм-ауты сервера (скажем, ваш API не отвечает в ожидаемое время. URLSession имеет время по умолчанию из 60 секунд) с конкретным кодом ошибки и сообщением.
Вы можете добавить закрытие завершения в displayMessage и вызвать ожидание.fulfill() в тесте. Другой совершенно другой подход заключается в реализации некоторого шаблона дизайна презентации, такого как координатор или ведущий. В этом случае все ваше представление пользовательского интерфейса будет абстрагироваться от неасинхронных методов.
performUIUpdate
поделитесь идеей, о которой вы подумали. Но не могли бы вы поделиться чем-то похожим на реальный код, который вы хотите протестировать? В противном случае мы предлагаем ответы на расплывчатый вопрос. Конкретизируйте вопрос. - person Jon Reid   schedule 03.11.2020