Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

UI-тесты в iOS-проекте / Михаил Домрачев (Improve Digital)

168 views

Published on

РИТ++ 2017, App's Conf
Зал Найроби, 6 июня, 12:00

Тезисы:
http://appsconf.ru/2017/abstracts/2821.html

- UI-тестами мы решали проблему быстрого поиска визуальных и навигационных несоответствий ввиду частых изменений общей кодовой базы и UI-элементов.
- В результате за несколько минут получаем скриншот-лист любого user journey и можем отправить его, при необходимости, как заказчику, так и дизайнеру.
- Мы всегда уверены в том, что если наши UI-тесты прошли, то мы имеем полноценно работающий роутинг.
- Как всегда, не обошлось без ложки дегтя. Recorder для генерации UI-тестов из XCode работает верно, но не учитывает особенностей вашего приложения, например, мультиязычность. Поделюсь советами, как сразу обходить стороной такие проблемы.

Published in: Engineering
  • Be the first to comment

  • Be the first to like this

UI-тесты в iOS-проекте / Михаил Домрачев (Improve Digital)

  1. 1. Домрачев Михаил iOS developer Improve Digital Новосибирск UI тесты в iOS проекте Есть ли профит и для чего их вообще внедряют?
  2. 2. А ты пишешь UI тесты ?
  3. 3. Как мы пришли к этому? • Проект активно живет и 
 развивается 2 года • Команда разработчиков
 увеличилась • Проект состоит 
 не из одного приложения • Core framework
  4. 4. Finance application
  5. 5. Skeleton screen Item - background color - imageName Item - title - title color - background color
  6. 6. Core framework App 30% Core framework 70% Core framework AppCore framework Application • Screen module • Any services • Utils • Design elements - xib - cell - view model • Override screen module • New screen modules • Override services • xib
  7. 7. Design like Lego • Берем ячейки из Core framework • Настраиваем их через View-Model’s • Заполняем таблицу экрана
  8. 8. Помоги Мише найти 10 отличий 1 1 2 2 3 3 4 4 Screen from Core framework Screen from 
 Core framework
  9. 9. Screen from Core framework Override screen from Core framework
  10. 10. Нельзя просто так взять и написать код идеально
  11. 11. Источники проблем • Новые разработчики • Общие ресурсы • Дизайнер забыл про единый стиль приложений
  12. 12. UI testing
  13. 13. Инструменты iOS UI testing
  14. 14. UI testing Стоимость
  15. 15. UI testing Стоимость Free Free Free
  16. 16. UI testing Стоимость Free Free Free Кроссплатфор- менный
  17. 17. UI testing Стоимость Free Free Free Кроссплатфор- менный iOS, Android iOS, Android
  18. 18. UI testing Стоимость Free Free Free Кроссплатфор- менный iOS, Android iOS, Android iOS
  19. 19. UI testing Стоимость Free Free Free Кроссплатфор- менный iOS, Android iOS, Android iOS Кодогенерация
  20. 20. UI testing Стоимость Free Free Free Кроссплатфор- менный iOS, Android iOS, Android iOS Кодогенерация + +
  21. 21. UI testing Стоимость Free Free Free Кроссплатфор- менный iOS, Android iOS, Android iOS Кодогенерация + - +
  22. 22. UI testing Стоимость Free Free Free Кроссплатфор- менный iOS, Android iOS, Android iOS Кодогенерация + - +
  23. 23. UI testing Стоимость Free Free Free Кроссплатфор- менный iOS, Android iOS, Android iOS Кодогенерация + - + Опыт
 использования
  24. 24. UI testing Стоимость Free Free Free Кроссплатфор- менный iOS, Android iOS, Android iOS Кодогенерация + - + Опыт
 использования - - +
  25. 25. iOS UI testing Accessibility +
  26. 26. XCTest UI-тесты базируются на трех классах: • XCUIApplication • XCUIElement • XCUIElementQuery
  27. 27. UI test recording
  28. 28. UI test recording XCUIApplication *app = app2; [app.buttons[@"start"] tap]; XCUIElement *element = [[[[app.otherElements containingType:XCUIElementTypeNavigationBar identifier:@“UIView”] childrenMatchingType:XCUIElementTypeOther].element childrenMatchingType:XCUIElementTypeOther].element childrenMatchingType:XCUIElementTypeOther].element; [element tap]; XCUIApplication *app2 = app; [app2.keys[@"t"] tap]; [app typeText:@“t"]; . . . [app typeText:@"c"]; [app2.keys[@“o"] [element tap];
  29. 29. UI test recording [app.buttons[@"start"] tap]; XCUIElement *element = [[[[app.otherElements containingType:XCUIElementTypeNavigationBar identifier:@“UIView”] childrenMatchingType:XCUIElementTypeOther].element childrenMatchingType:XCUIElementTypeOther].element childrenMatchingType:XCUIElementTypeOther].element; [element tap]; XCUIApplication *app = app2; XCUIApplication *app = app2; [app2.keys[@"t"] tap]; [app typeText:@“t"]; . . . [app typeText:@"c"]; [app2.keys[@“o"] [element tap];
  30. 30. UI test recording XCUIElement *element = [[[[app.otherElements containingType:XCUIElementTypeNavigationBar identifier:@“UIView”] childrenMatchingType:XCUIElementTypeOther].element childrenMatchingType:XCUIElementTypeOther].element childrenMatchingType:XCUIElementTypeOther].element; XCUIApplication *app = app2;
 [app.buttons[@"start"] tap]; XCUIApplication *app = app2; [app2.keys[@"t"] tap]; [app typeText:@“t"]; . . . [app typeText:@"c"]; [app2.keys[@“o"] [element tap]; [element tap];
  31. 31. UI test recording • Не всегда читаемый код • Не всегда работающий код Есть минусы: • Много кода • Переиспользование функционала
  32. 32. От теории к практике • Stub manager • Page object pattern • Snapshot
  33. 33. Stub manager class NSURLProtocol -  позволяет предопределить работу системы загрузки URL для iOS 1.  Создать свой класс 2.  Зарегистрировать его Делается в два действия:
  34. 34. Stub manager + (BOOL)canInitWithRequest:(NSURLRequest *)request class MyNSURLProtocol
  35. 35. Stub manager + (BOOL)canInitWithRequest:(NSURLRequest *)request class MyNSURLProtocol - (void)startLoading
  36. 36. Stub manager + (BOOL)canInitWithRequest:(NSURLRequest *)request class MyNSURLProtocol - (void)startLoading - (void)stopLoading
  37. 37. } Stub manager NSData *cachedData = берем данные из кэша if (cachedData) { [self.client URLProtocol: self didFailWithError: создаем ошибку - (void)startLoading NSHTTPURLResponse *response = создаем свой response
 [self.client URLProtocol: didReceiveResponse: cacheStoragePolicy:]; [self.client URLProtocol:self didLoadData: cachedData]; [self.client URLProtocolDidFinishLoading: self]; } else {
  38. 38. Stub manager class MyStubManager NSURLSessionConfiguration setProtocolClasses: @[[MyNSURLProtocol class]]
  39. 39. Page object pattern
  40. 40. Page object pattern
  41. 41. Page object pattern Profit • Читабельность кода • Переиспользование функционала • Централизация интерфейса пользователя
  42. 42. XCTestUtils @interface FFElements : NSObject - (XCUIElement *)objectForKeyedSubscript:(NSString *)key; - (XCUIElement *)objectAtIndexedSubscript:(NSUInteger)index; @end
  43. 43. XCTestUtils@interface FFElements : NSObject - (XCUIElement *)objectForKeyedSubscript:(NSString *)key; - (XCUIElement *)objectAtIndexedSubscript:(NSUInteger)index; @end @interface XCTestCase (Elements) @property (nonatomic,readonly) FFElements *textFields; @property (nonatomic,readonly) FFElements *buttons; @property (nonatomic,readonly) FFElements *labels; @property (nonatomic,readonly) FFElements *cells; - (void)wait:(NSTimeInterval)interval; @end
  44. 44. XCTestUtils@interface FFElements : NSObject - (XCUIElement *)objectForKeyedSubscript:(NSString *)key; - (XCUIElement *)objectAtIndexedSubscript:(NSUInteger)index; @end @interface XCTestCase (Elements) @property (nonatomic,readonly) FFElements *textFields; @property (nonatomic,readonly) FFElements *buttons; @property (nonatomic,readonly) FFElements *labels; @property (nonatomic,readonly) FFElements *cells; - (void)wait:(NSTimeInterval)interval; @end @interface XCUIElement (Utils) @property (nonatomic) NSString *pasteText; + (void)forceTap; @end
  45. 45. XCTestUtils @interface FFElements : NSObject - (XCUIElement *)objectForKeyedSubscript:(NSString *)key; - (XCUIElement *)objectAtIndexedSubscript:(NSUInteger)index; @end @interface XCTestCase (Elements) @property (nonatomic,readonly) FFElements *textFields; @property (nonatomic,readonly) FFElements *buttons; @property (nonatomic,readonly) FFElements *labels; @property (nonatomic,readonly) FFElements *cells; - (void)wait:(NSTimeInterval)interval; @end @interface XCUIElement (Utils) @property (nonatomic) NSString *pasteText; +(void)forceTap; @end
  46. 46. Тесты готовы А что с ними делать то?
  47. 47. Fastlane
  48. 48. Snapshot Какая от него польза? • Делает скриншоты • Прогоняет тесты • Легко интегрируется с CI
  49. 49. Snapshot Как его внедрить? • fastlane snapshot init • Настроить Snapfile • В тестах указать места, где бы вы хотели получить скриншот
  50. 50. Snapshot Snapfile devices ([ "iPhone 6s" ])
  51. 51. Snapshot Snapfile devices ([ "iPhone 6s" ]) scheme: “our scheme”
  52. 52. Snapshot Snapfile devices ([ "iPhone 6s" ]) scheme: “our scheme” output_directory: “./path/.”
  53. 53. Snapshot Snapfile devices ([ "iPhone 6s" ]) scheme: “our scheme” output_directory: “./path/.” stop_after_first_error: Bool
  54. 54. Snapshot Snapfile devices ([ "iPhone 6s" ]) scheme: “our scheme” output_directory: “./path/.” reinstall_app: Bool stop_after_first_error: Bool
  55. 55. Snapshot Snapfile devices ([ "iPhone 6s" ]) scheme: “our scheme” output_directory: “./path/.” reinstall_app: Bool clear_previous_screenshots: Bool stop_after_first_error: Bool
  56. 56. Snapshot Snapfile devices ([ "iPhone 6s" ]) scheme: “our scheme” output_directory: “./path/.” reinstall_app: Bool clear_previous_screenshots: Bool erase_simulator: Bool stop_after_first_error: Bool
  57. 57. Snapshot Snapfile devices ([ "iPhone 6s" ]) scheme: “our scheme” output_directory: “./path/.” reinstall_app: Bool clear_previous_screenshots: Bool languages ([ “en-US” ]) stop_after_first_error: Bool erase_simulator: Bool
  58. 58. Time for screenshot • Добавить SnaphotHelper.swift в таргет с тестами • Вызвать внутри метода setup(): [Snapshot setupSnapshot:app] • [Snapshot snapshot:@“Name screen" waitForLoadingIndicator:YES];
  59. 59. Snapshot
  60. 60. Snapshot
  61. 61. Обрати внимание! • Stub manager • Page object pattern • Snapshot
  62. 62. Мы получили • Уменьшили количество багов с дизайном почти до 0 • Появилась проверка правильного роутинга в приложении • Внедрили инструмент для быстрой генерации всех скриншотов приложения и интегрировали тесты с CI
  63. 63. Минусы • Время на внедрение UI тестов • Поддержка тестов при рефакторинге
  64. 64. Использовать тесты стоит • Если у вас долгосрочный проект • Постоянный диалог по поводу пиксель перфект • Сложная логика навигации в приложении
  65. 65. СПАСИБО Домрачев Михаил iOS developer Improve Digital Новосибирск domrachev@improveitgroup.com misha.domrachev

×