Использование тестов пользовательского интерфейса Xcode для проверки поведения базового фреймворка

Я создаю платформу iOS, которая собирает информацию о различных событиях в приложении iOS и выполняет локальный и удаленный анализ. Некоторые из этих событий нельзя протестировать вне приложения: например, переходы контроллера представления. Чтобы протестировать эти события, я создал тестовое приложение для iOS и хотел бы использовать тесты пользовательского интерфейса Xcode для:

  • Инициируйте переход контроллера представления, например, нажав кнопку, которая нажимает VC на контроллер навигации, или переключившись на другую вкладку в контроллере вкладок.

  • Убедитесь, что платформа смогла обнаружить переходы контроллера представления и сгенерировать необходимые события (например, отправить их на сервер).

Проблема в том, что тесты пользовательского интерфейса Xcode выполняются вне приложения, поэтому я не могу получить доступ к каким-либо общим объектам, чтобы убедиться, что инфраструктура работает правильно. Я также не могу вставлять какие-либо фиктивные объекты, потому что, опять же, у меня нет доступа к фреймворку, загруженному в процессе приложения.

Я дошел до того, что попытался загрузить фреймворк в своего рода тестовом режиме и обновить метку с некоторыми результатами, которые затем сможет прочитать тест пользовательского интерфейса. Но даже это сложно, потому что XCUIElement не дает мне фактический текст элемента; Я могу запрашивать только элементы с предопределенным текстом. Я могу работать с этим, но это кажется очень окольным подходом к чему-то, что должно быть довольно тривиальным.

Существуют ли какие-либо лучшие альтернативы для тестирования событий, которые можно смоделировать только в работающем приложении, а затем для проверки того, что моя среда способна их обнаруживать и обрабатывать? В дополнение к просмотру переходов контроллера меня также интересуют сенсорные взаимодействия, события акселерометра и другие функции, которые нельзя смоделировать вне приложения. Естественно, я могу имитировать эти события для модульного тестирования, но я также хотел бы автоматически проверять, что, когда эти события происходят в реальном приложении, в моей среде генерируются правильные ответы.


person Sasha Goldshtein    schedule 28.07.2016    source источник
comment
Рассматривали ли вы тесты пользовательского интерфейса для сенсорных событий и переходов? Может быть лучшим выбором, чем модульные тесты, в зависимости от того, что вам нужно.   -  person Robert Gummesson    schedule 28.07.2016
comment
@Robert: я говорю о тестах пользовательского интерфейса, а не о модульных тестах. Я хочу использовать тесты пользовательского интерфейса, чтобы нажать кнопку, которая запускает переход VC, а затем увидеть влияние этого перехода не на элементы пользовательского интерфейса, а скорее на внутренние компоненты моего фреймворка.   -  person Sasha Goldshtein    schedule 28.07.2016
comment
Знаете ли вы, что модульные тесты iOS (XCTest) фактически запускают приложение? Вы можете создавать полутесты пользовательского интерфейса, поскольку вы не будете эмулировать пользовательские события, а напрямую будете вызывать методы, которые вызывают переходы.   -  person Ohad Schneider    schedule 28.07.2016
comment
@OhadSchneider: Да, но это не то, чего я хочу. Я хочу протестировать фактические переходы пользовательского интерфейса, события акселерометра, касания и так далее. Конечно, у меня также есть модульные тесты, и они проверяют логику изолированно. То, что вы предлагаете, находится где-то посередине, и я бы тоже не хотел вкладываться в это.   -  person Sasha Goldshtein    schedule 28.07.2016


Ответы (2)


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

Мониторинг сети

Библиотека позволяет вашей тестовой цели собирать сетевые вызовы, вызванные в приложении. Использование довольно прямолинейно:

func testThatNetworkAfterEvent() {
  // Use SBTUITunneledApplication instead of XCUIApplication
  let app = SBTUITunneledApplication()
  app.launchTunnelWithOptions([SBTUITunneledApplicationLaunchOptionResetFilesystem]) {
      // do additional setup before the app launches
      // i.e. prepare stub request, start monitoring requests
  }

  app.monitorRequestsWithRegex("(.*)myserver(.*)") // monitor all requests containing myserver

  // 1. Interact with UI tapping elements that generate your events

  // 2. Wait for events to be sent. This could be determined from the UI (a UIActivitiIndicator somewhere in your app?) or ultimately if you have no other option with an NSThread.sleepfortimeinterval

  // 3. Once ready flush calls and get the list of requests
  let requests: [SBTMonitoredNetworkRequest] = app.monitoredRequestsFlushAll()

  for request in requests {
      let requestBody = request.request!.HTTPBody // HTTP Body in POST request?
      let responseJSON = request.responseJSON
      let requestTime = request.requestTime // How long did the request take?
  }
}

Преимущество сетевого мониторинга в том, что весь тестовый код и логика содержатся в вашей тестовой цели.

Пользовательский блок кода

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

Зарегистрируйте блок кода в целевом приложении

SBTUITestTunnelServer.registerCustomCommandNamed("myCustomCommandKey") {
    injectedObject in
    // this block will be invoked from app.performCustomCommandNamed()

    return "any object you like"
}

и вызвать его из тестовой цели

func testThatNetworkAfterEvent() {
  let app = ....

  // at the right time
  let objFromBlock = app.performCustomCommandNamed("myCustomCommand", object: someObjectToInject)
}

В настоящее время вы можете вставлять данные только из целевого объекта теста -> целевого приложения. Если вам нужно наоборот, вы можете сохранить эти данные в NSUserDefaults и получить их с помощью метода userDefaultsObjectForKey() приложения SBTUIApplication.

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

ИЗМЕНИТЬ

Я обновил библиотеку, и, начиная с версии 0.9.23, теперь вы можете вернуть любой объект из блока в тестовую цель. Больше нет необходимости в обходных путях!

person Tomas Camin    schedule 28.07.2016
comment
Просто чтобы уточнить, что наоборот - могу ли я использовать registerCustomCommandNamed для извлечения данных, скажем, из одноэлементного объекта в среде, загруженной в тестовое приложение? - person Sasha Goldshtein; 28.07.2016
comment
Похоже, да. Спасибо - это именно то, что мне было нужно! - person Sasha Goldshtein; 28.07.2016

Не очень автоматизированный способ сделать что-то, но используйте точки останова в качестве промежуточного способа, по крайней мере, до тех пор, пока вы не начнете что-то автоматически. Вы можете добавить символическую точку останова для обращения к методам пользовательского интерфейса, таким как [UIWindow setRootViewController:], viewWillAppear и т. д.

Вы можете явно определить модули, условия и действия, чтобы всякий раз, когда кто-то просматривает ваш VC, выполнялись некоторые действия, которые могут быть вам полезны.

Если вы можете напечатать что-то из своей структуры, используя простое «po myEvent» для некоторого viewDidAppear, все готово. Я никогда не пробовал использовать более причудливые методы в качестве действия, но это тоже можно сделать.

person Nili    schedule 28.07.2016
comment
Конечно, я могу использовать точки останова и пошагово выполнять код для ручного тестирования. Хотя я не большой фанат. Я очень хочу, чтобы это происходило автоматически. - person Sasha Goldshtein; 28.07.2016