#ios #swift #xctest #xctestcase #xctestexpectation
#iOS #swift #xctest #xctestcase #xctestexpectation
Вопрос:
Мне нужно написать модульный тест для следующего метода
func setLabelText(msg: String) {
DispatchQueue.main.async {
self.label.text = msg
}
}
Комментарии:
1. В вашем
performUIUpdate
примере поделитесь идеей, о которой вы подумали. Но не могли бы вы поделиться чем-то похожим на фактический код, который вы хотите протестировать? В противном случае мы предлагаем ответы на неопределенный вопрос. Сделайте вопрос более конкретным.2. Мне нужно написать тест для следующего метода func setLabelText(msg: String) { DispatchQueue.main.async { self. label.text = сообщение } }
3. Можете ли вы отредактировать свой вопрос, чтобы разместить это там?
4. Обновлено @JonReid
Ответ №1:
Давайте предположим, что ваша тестовая настройка уже создает контроллер представления и вызывает 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")
}
Ответ №2:
Здравствуйте, на мой взгляд, использование XCTestExpectation
может принести пользу, поскольку этот API предназначался для тестирования асинхронных операций (подробнее об этом вы можете прочитать здесь)
Что касается сравнения между ними, единственное, о чем я мог подумать, это то, что с XCTestExpectation
вами вы сможете протестировать тайм-ауты сервера (скажем, ваш api не отвечает в ожидаемое время. URLSession имеет время ожидания по умолчанию 60 секунд) с определенным кодом ошибки и сообщением.
Ответ №3:
Вы можете добавить закрытие завершения в DisplayMessage и ожидание вызова.выполнить () в тесте. Другой совершенно другой подход заключается в реализации некоторого шаблона проектирования презентации, такого как координатор или докладчик. В этом случае все ваше представление пользовательского интерфейса будет абстрагировано от неасинхронных методов.