Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Единственное, что вам нужно для UI-тестирования

916b2b50d1c958f9ed1c008623065b8a?s=47 MOSDROID
September 13, 2019

Единственное, что вам нужно для UI-тестирования

Егор Курников, Лаборатория Касперского at #MOSDROID 19 Potassium [in KasperskyLab HeadQuarter]

При написании UI-тестов для мобильных Android-приложений возникает множество различных проблем. Часть из них может быть решена с помощью ряда уже существующих инструментов, другая часть каждой компанией решается по-своему, эти решения довольно специфичны и не могут быть транслированы. Остальные проблемы остаются без решения.

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

916b2b50d1c958f9ed1c008623065b8a?s=128

MOSDROID

September 13, 2019
Tweet

Transcript

  1. None
  2. Зачем нам фреймворк? 2

  3. Зачем нам фреймворк? ➔ Как начать писать автотесты? 2

  4. ➔ Как начать писать автотесты? ➔ Какие инструменты выбрать? Зачем

    нам фреймворк? 2
  5. UiAutomator Cappuccino Barista

  6. ➔ Как начать писать автотесты? ➔ Какие инструменты выбрать? ➔

    Что делать если нужного инструмента нет? Зачем нам фреймворк? 4
  7. ➔ Как начать писать автотесты? ➔ Какие инструменты выбрать? ➔

    Что делать если нужного инструмента нет? Зачем нам фреймворк? 5
  8. None
  9. @RunWith(AndroidJUnit4::class) class TypeLoginTest { @get:Rule val activityTestRule = ActivityTestRule(MainActivity::class.java) @Test

    fun test() { onView(withId(R.id.login_input_view)) .perform(typeText(“MyLogin”)) .check(matches(withText(“MyLogin”)) } } 7
  10. @RunWith(AndroidJUnit4::class) class TypeLoginTest { @get:Rule val activityTestRule = ActivityTestRule(MainActivity::class.java) @Test

    fun test() { onView(withId(R.id.login_input_view)) .perform(typeText(“MyLogin”)) .check(matches(withText(“MyLogin”)) } } 7
  11. @RunWith(AndroidJUnit4::class) class TypeLoginTest { @get:Rule val activityTestRule = ActivityTestRule(MainActivity::class.java) @Test

    fun test() { onView(withId(R.id.login_input_view)) .perform(typeText(“MyLogin”)) .check(matches(withText(“MyLogin”)) } } 7
  12. @RunWith(AndroidJUnit4::class) class TypeLoginTest { @get:Rule val activityTestRule = ActivityTestRule(MainActivity::class.java) @Test

    fun test() { onView(withId(R.id.login_input_view)) .perform(typeText(“MyLogin”)) .check(matches(withText(“MyLogin”)) } } 7
  13. onView(ViewMatcher) ViewInteraction check(ViewAssertion) perform(ViewAction) 8

  14. 9

  15. Чего мы хотим? 10

  16. ➔ Хорошая читаемость Чего мы хотим? 10

  17. @Test fun espressoTest() { onView(allOf(allOf(withId(R.id.espresso), isDescendantOfA(withId(R.id.coffee_variates))), isDescendantOfA(withId(R.id.content)))) .check(matches(withEffectiveVisibility(View.VISIBLE))) } 11

  18. @Test fun espressoTest() { onView(allOf(allOf(withId(R.id.espresso), isDescendantOfA(withId(R.id.coffee_variates))), isDescendantOfA(withId(R.id.content)))) .check(matches(withEffectiveVisibility(View.VISIBLE))) }

  19. ➔ Хорошая читаемость ➔ Стабильность Чего мы хотим? 12

  20. ➔ Хорошая читаемость ➔ Стабильность ➔ Логирование Чего мы хотим?

    12
  21. None
  22. ➔ Хорошая читаемость ➔ Стабильность ➔ Логирование ➔ Скриншоты Чего

    мы хотим? 14
  23. ➔ Хорошая читаемость ➔ Стабильность ➔ Логирование ➔ Скриншоты ➔

    Работа с OS Android Чего мы хотим? 14
  24. ➔ Хорошая читаемость ➔ Стабильность ➔ Логирование ➔ Скриншоты ➔

    Работа с OS Android ➔ Архитектура Чего мы хотим? 14
  25. 15

  26. ➔ Хорошая читаемость ➔ Стабильность ➔ Логирование ➔ Скриншоты ➔

    Работа с OS Android ➔ Архитектура Чего мы хотим? 16
  27. None
  28. @Test fun espressoTest() { onView(allOf(allOf(withId(R.id.espresso), isDescendantOfA(withId(R.id.coffee_variates))), isDescendantOfA(withId(R.id.content)))) .check(matches(withEffectiveVisibility(View.VISIBLE))) } 18

  29. @Test fun kakaoTest() { mainScreen { myView.isVisible() } } 18

  30. val button = KButton { withId(R.id.my_btn) } val textView =

    KTextView { withBackgroundColor(R.color.primary) isDescendantOfA { withId(R.id.content) withMatcher(MyCustomMatcher()) } } KView 19
  31. val button = KButton { ... } val textView =

    KTextView { ... } btn.click() textView { isVisible() hasText(R.string.title) } KView 20
  32. open class KBaseView<out T> : BaseActions, BaseAssertions { override val

    view: ViewInteraction ... } 21
  33. open class KBaseView<out T> : BaseActions, BaseAssertions { override val

    view: ViewInteraction ... } 21
  34. open class KBaseView<out T> : BaseActions, BaseAssertions { override val

    view: ViewInteraction ... } 21
  35. interface BaseActions { val view: ViewInteraction fun click() { ...

    } fun doubleClick() { ... } fun longClick() { ... } fun scrollTo() { view.perform( ViewActions.scrollTo() ) } ... } interface BaseAssertions { val view: ViewInteraction fun isDisplayed() { ... } fun isVisible() { ... } fun hasBackgroundColor() { ... } fun isClickable() { view.check( ViewAssertions.matches( ViewMatchers.isClickable() ) ) } ... } 22
  36. interface BaseActions { val view: ViewInteraction fun click() { ...

    } fun doubleClick() { ... } fun longClick() { ... } fun scrollTo() { view.perform( ViewActions.scrollTo() ) } ... } interface BaseAssertions { val view: ViewInteraction fun isDisplayed() { ... } fun isVisible() { ... } fun hasBackgroundColor() { ... } fun isClickable() { view.check( ViewAssertions.matches( ViewMatchers.isClickable() ) ) } ... } 22
  37. interface BaseActions { val view: ViewInteraction fun click() { ...

    } fun doubleClick() { ... } fun longClick() { ... } fun scrollTo() { view.perform( ViewActions.scrollTo() ) } ... } interface BaseAssertions { val view: ViewInteraction fun isDisplayed() { ... } fun isVisible() { ... } fun hasBackgroundColor() { ... } fun isClickable() { view.check( ViewAssertions.matches( ViewMatchers.isClickable() ) ) } ... } 22
  38. class FormScreen : Screen<FormScreen>() { val login = KEditText {

    withId(R.id.login) } val password = KEditText { withId(R.id.password) } val submitBtn = KButton { withText(“Submit”) } val backBtn = KButton { withId(R.id.back) } } Submit login Sign in password Page object 23
  39. @Test fun test() { MainScreen() { nextButton { isVisible() click()

    } } SecondScreen() { button1.isVisible() button2.click() } ThirdScreen() { editText { isVisible() hasText(R.string.my_text) } } } 24
  40. ➔ Хорошая читаемость ➔ Стабильность ➔ Логирование ➔ Скриншоты ➔

    Работа с OS Android ➔ Архитектура Чего мы хотим? 25
  41. ➔ Хорошая читаемость ➔ Стабильность ➔ Логирование ➔ Скриншоты ➔

    Работа с OS Android ➔ Архитектура Чего мы хотим? 25
  42. Interceptor Caller Called 26

  43. onView(ViewMatcher) ViewInteraction check(ViewAssertion) perform(ViewAction) 27

  44. onView(ViewMatcher) 28 interaction.perform(ViewAction)

  45. onView(ViewMatcher) interaction.perform(ViewAction) 29 Interceptor

  46. open class KBaseView<out T> : BaseActions, BaseAssertions { override val

    view: ViewInteraction ... } 30
  47. open class KBaseView<out T> : BaseActions, BaseAssertions { override val

    view: ViewInteractionDelegate ... } 30
  48. @Before fun setup() { Kakao.intercept { onViewInteraction { onCheck {

    interaction, assertion -> Log.d("KAKAO", "$interaction is checking $assertion") } onPerform { interaction, action -> Log.d("KAKAO", "$interaction is performing $action") } onAll { interaction -> Log.d("KAKAO", "$interaction") } } onDataInteraction { ... } onWebInteraction { ... } } } 31
  49. @Before fun setup() { Kakao.intercept { onViewInteraction { onCheck {

    interaction, assertion -> Log.d("KAKAO", "$interaction is checking $assertion") } onPerform { interaction, action -> Log.d("KAKAO", "$interaction is performing $action") } onAll { interaction -> Log.d("KAKAO", "$interaction") } } onDataInteraction { ... } onWebInteraction { ... } } } 31
  50. @Before fun setup() { Kakao.intercept { onViewInteraction { onCheck {

    interaction, assertion -> Log.d("KAKAO", "$interaction is checking $assertion") } onPerform { interaction, action -> Log.d("KAKAO", "$interaction is performing $action") } onAll { interaction -> Log.d("KAKAO", "$interaction") } } onDataInteraction { ... } onWebInteraction { ... } } } 31
  51. @Before fun setup() { Kakao.intercept { onViewInteraction { onCheck {

    interaction, assertion -> Log.d("KAKAO", "$interaction is checking $assertion") } onPerform { interaction, action -> Log.d("KAKAO", "$interaction is performing $action") } onAll { interaction -> Log.d("KAKAO", "$interaction") } } onDataInteraction { ... } onWebInteraction { ... } } } 31
  52. @Before fun setup() { Kakao.intercept { onViewInteraction { onCheck {

    interaction, assertion -> Log.d("KAKAO", "$interaction is checking $assertion") } onPerform { interaction, action -> Log.d("KAKAO", "$interaction is performing $action") } onAll { interaction -> Log.d("KAKAO", "$interaction") } } onDataInteraction { ... } onWebInteraction { ... } } } 31
  53. @Before fun setup() { Kakao.intercept { onViewInteraction { onCheck {

    interaction, assertion -> Log.d("KAKAO", "$interaction is checking $assertion") } onPerform { interaction, action -> Log.d("KAKAO", "$interaction is performing $action") } onAll { interaction -> Log.d("KAKAO", "$interaction") } } onDataInteraction { ... } onWebInteraction { ... } } } 31
  54. @Before fun setup() { Kakao.intercept { onViewInteraction { onCheck {

    interaction, assertion -> Log.d("KAKAO", "$interaction is checking $assertion") } onPerform { interaction, action -> Log.d("KAKAO", "$interaction is performing $action") } onAll { interaction -> Log.d("KAKAO", "$interaction") } } onDataInteraction { ... } onWebInteraction { ... } } } 31
  55. mainScreen.intercept SecondScreen KView KView KView Kakao.intercept KView KView myView.intercept 32

  56. onView(ViewMatcher) interaction.perform(ViewAction) 33 Interceptor

  57. Flaky safety fun <T> flakySafely(action: () -> T): T {

    var cachedException: Throwable var startTime = System.currentTimeMillis() do { try { return action.invoke() } catch (e: Throwable) { Thread.sleep(intervalMs) cachedException = e } } while(System.currentTimeMillis() - startTime <= timeoutMs) throw cachedException } 34
  58. onView(ViewMatcher) interaction.perform(ViewAction) 35 Interceptor

  59. onView(ViewMatcher) flakySafely { } Interceptor interaction.perform(ViewAction) 36

  60. onView(ViewMatcher) flakySafely { } BehaviorInterceptor interaction.perform(ViewAction) 36

  61. onView(ViewMatcher) FlakyBehaviorInterceptor interaction.perform(ViewAction) AutoScrollBehaviorInterceptor 37

  62. ➔ Хорошая читаемость ➔ Стабильность ➔ Логирование ➔ Скриншоты ➔

    Работа с OS Android ➔ Архитектура Чего мы хотим? 38
  63. ➔ Хорошая читаемость ➔ Стабильность ➔ Логирование ➔ Скриншоты ➔

    Работа с OS Android ➔ Архитектура Чего мы хотим? 38
  64. BehaviorInterceptors 39 onView(ViewMatcher) check(ViewAssertion) perform(ViewAction) ViewInteraction

  65. 40 interaction.perform(ViewAction) onView(ViewMatcher) viewAction.perform(UiContorller, View) BehaviorInterceptors

  66. 40 interaction.perform(ViewAction) onView(ViewMatcher) viewAction.perform(UiContorller, View) BehaviorInterceptors

  67. Interceptor Log.d(TAG, “${viewAction.descr} on ${view.descr}”) 41 viewAction.perform(UiController, View) interaction.perform(ViewAction) BehaviorInterceptors

  68. WatcherInterceptor Log.d(TAG, “${viewAction.descr} on ${view.descr}”) 41 viewAction.perform(UiController, View) interaction.perform(ViewAction) BehaviorInterceptors

  69. WatcherInterceptor Log.d(TAG, “${viewAction.descr} on ${view.descr}”) 41 viewAction.perform(UiController, View) interaction.perform(ViewAction) BehaviorInterceptors

  70. WatcherInterceptor Log.d(TAG, “${viewAction.descr} on ${view.descr}”) 42 viewAction.perform(UiController, View) BehaviorInterceptors interaction.perform(ViewActionProxy)

  71. class ViewActionProxy( private val viewAction: ViewAction ): ViewAction { fun

    perform(ui: UiController, view: View) { Log.d(TAG, “${viewAction.descr} on ${view.descr}”) viewAction.perform(ui, view) } } interaction.perform(ViewActionProxy(viewAction)) 43 BehaviorInterceptors
  72. class ViewActionProxy( private val viewAction: ViewAction ): ViewAction { fun

    perform(ui: UiController, view: View) { Log.d(TAG, “${viewAction.descr} on ${view.descr}”) viewAction.perform(ui, view) } } interaction.perform(ViewActionProxy(viewAction)) 43 BehaviorInterceptors
  73. class ViewActionProxy( private val viewAction: ViewAction ): ViewAction { fun

    perform(ui: UiController, view: View) { watcherInterceptors.forEach { it.intercept(ui, view) } viewAction.perform(ui, view) } } interaction.perform(ViewActionProxy(viewAction)) 44 BehaviorInterceptors
  74. ViewActionProxy WatcherInterceptors viewAction.perform(UiController, View) interaction.perform(ViewActionProxy) 45 BehaviorInterceptors

  75. ➔ Хорошая читаемость ➔ Стабильность ➔ Логирование ➔ Скриншоты ➔

    Работа с OS Android ➔ Архитектура Чего мы хотим? 46
  76. ➔ Хорошая читаемость ➔ Стабильность ➔ Логирование ➔ Скриншоты ➔

    Работа с OS Android ➔ Архитектура Чего мы хотим? 46
  77. ➔ Хорошая читаемость ➔ Стабильность ➔ Логирование ➔ Скриншоты ➔

    Работа с OS Android ➔ Архитектура Чего мы хотим? 46
  78. ➔ Accessibility ➔ Activities ➔ Apps ➔ Exploit ➔ Files

    ➔ Keyboard ➔ Location ➔ Network ➔ Permissions ➔ Phone ➔ Screenshots Device 47
  79. ➔ Accessibility ➔ Activities ➔ Apps ➔ Exploit ➔ Files

    ➔ Keyboard ➔ Location ➔ Network ➔ Permissions ➔ Phone ➔ Screenshots interface Apps { fun install(apkPath: String) fun uninstall(pckgName: String) fun launch(pckgName: String, data: Uri?) fun openRecent(contentDescr: String) fun kill(pckgName: String) } Device 48
  80. ➔ Accessibility ➔ Activities ➔ Apps ➔ Exploit ➔ Files

    ➔ Keyboard ➔ Location ➔ Network ➔ Permissions ➔ Phone ➔ Screenshots Device interface Files { fun push( serverPath: String, devicePath: String ) fun remove(path: String) } 49
  81. ➔ Accessibility ➔ Activities ➔ Apps ➔ Exploit ➔ Files

    ➔ Keyboard ➔ Location ➔ Network ➔ Permissions ➔ Phone ➔ Screenshots Device interface Network { fun enable() fun disable() fun enableWiFi() fun disableWiFi() } 50
  82. Что умеет ADB? ➔ Установка apk ➔ push/pull ➔ Выставление

    системных настроек 51
  83. Что умеет ADB? ➔ Установка apk ➔ push/pull ➔ Выставление

    системных настроек adb emu ➔ Установка геопозиции ➔ Выставление скорости сети, задержки ➔ Симуляция вызовов ➔ Выставление значений акселерометра ➔ Имитация работы с отпечатком пальца 51
  84. Client app App Test app Tests HOST 52

  85. Virtual routing 10.0.2.2 - Special alias to your loopback interface

    (i.e. 127.0.0.1 on your development machine) 53
  86. Client app HOST App Server Test app Tests 54

  87. Client app HOST App Server Test app Tests GET 10.0.2.2:8080/?cmd=”adb

    ...” adb ... 54
  88. None
  89. $ adb shell generic_x86:/ # ifconfig radio0 Link encap:Ethernet HWaddr

    7a:7f:dd:f9:55:3r inet addr:192.168.200.2 Bcast:192.168.200.255 Mask 255.255.255.0 inet6 addr: fec0::4072:df55:c06e:32bc/64 Scope: Site inet6 addr: fe80::787f:bcff:fef0:444e/64 Scope: Link inet6 addr: fec0::787f:bcff:fef0:444e/64 Scope: Site UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:1191 errors:0 dropped:0 overruns:0 frame:0 TX packets:1314 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 RX bytes:436242 TX bytes: 127317 lo Link encap:Local loopback inet addr:127.0.0.1 Mask 255.0.0.0 inet6 addr: ::1/128 Scope: Host UP LOOPBACK RUNNING MULTICAST MTU:655536 Metric:1 RX packets:38 errors:0 dropped:0 overruns:0 frame:0 TX packets:38 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 RX bytes:4542 TX bytes: 4542 wlan0 Link encap:Ethernet HWaddr 01:22:33:44:55:66 Driver mac80211_hwsim inet addr:192.168.232.2 Bcast:192.168.239.255 Mask 255.255.248.0 inet6 addr: fe80::ff:fe44:5566/64 Scope: Link inet6 addr: fec0::61d3:bc35:c84d:1be3/64 Scope: Site inet6 addr: fec0::ff:fe44:5566/64 Scope: Site UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:50545 errors:0 dropped:0 overruns:0 frame:0 TX packets:34184 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 RX bytes:52675660 TX bytes: 6458247 56
  90. $ adb shell generic_x86:/ # ifconfig lo Link encap:Local loopback

    inet addr:127.0.0.1 Mask 255.0.0.0 inet6 addr: ::1/128 Scope: Host UP LOOPBACK RUNNING MULTICAST MTU:655536 Metric:1 RX packets:38 errors:0 dropped:0 overruns:0 frame:0 TX packets:38 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 RX bytes:4542 TX bytes: 4542 57
  91. None
  92. Port forwarding ➔ adb forward tcp:6100 tcp:8500 HOST localhost:8500 GET

    localhost:6100 localhost:6100 59
  93. Client app HOST App Test app Tests 60 Client localhost:

    6100
  94. Client app App Test app Server localhost:8500 Tests HOST Client

    localhost: 6100 61
  95. Client app App adb forward tcp:6100 tcp:8500 Client localhost: 8500

    Test app Tests HOST 62 Client localhost: 6100 Server localhost:8500
  96. Client app App Test app Tests HOST 63 Client localhost:

    6100 Client localhost: 8500 Server localhost:8500
  97. adb forward ... Cli ent app Server App Cli ent

    app Server App Cli ent app Server App adb forward ... adb forward ... HOST 64 Client localhost: 6100
  98. class AdbServer { fun performCmd( vararg commands: String ): String<String>

    fun performAdb( vararg commands: String ): String<String> fun perfromShell( vararg commands: String ): String<String> fun disconnectServer() } 1. Connect 2. performAdb("...") 3. Execute "..." 5. Disconnect 65 Client localhost: 8500 Server localhost:8500 4. Command complete
  99. ➔ Хорошая читаемость ➔ Стабильность ➔ Логирование ➔ Скриншоты ➔

    Работа с OS Android ➔ Архитектура Чего мы хотим? 66
  100. ➔ Хорошая читаемость ➔ Стабильность ➔ Логирование ➔ Скриншоты ➔

    Работа с OS Android ➔ Архитектура Чего мы хотим? 66
  101. Kaspresso

  102. @RunWith(AndroidJUnit4::class) class OpenHomeScreenTest { private val mainScreen = MainScreen() private

    val homeScreen = HomeScreen() @get:Rule val activityTestRule = ActivityTestRule(MainActivity::class.java, true, false) @Test fun test() { ... } } 68 Kaspresso
  103. @RunWith(AndroidJUnit4::class) class OpenHomeScreenTest { private val mainScreen = MainScreen() private

    val homeScreen = HomeScreen() @get:Rule val activityTestRule = ActivityTestRule(MainActivity::class.java, true, false) @get:Rule val testCaseRule = TestCaseRule(javaClass.simpleName) @Test fun test() { ... } } 69 Kaspresso
  104. @RunWith(AndroidJUnit4::class) class OpenHomeScreenTest : TestCase() { private val mainScreen =

    MainScreen() private val homeScreen = HomeScreen() @get:Rule val activityTestRule = ActivityTestRule(MainActivity::class.java, true, false) @Test fun test() { ... } } 70 Kaspresso
  105. @RunWith(AndroidJUnit4::class) class OpenHomeScreenTest : TestCase( Kaspresso.Builder().apply { ... } )

    { private val mainScreen = MainScreen() private val homeScreen = HomeScreen() @get:Rule val activityTestRule = ActivityTestRule(MainActivity::class.java, true, false) @Test fun test() { ... } } 71 Kaspresso
  106. @RunWith(AndroidJUnit4::class) class OpenHomeScreenTest : TestCase( Kaspresso.Builder.default() ) { private val

    mainScreen = MainScreen() private val homeScreen = HomeScreen() @get:Rule val activityTestRule = ActivityTestRule(MainActivity::class.java, true, false) @Test fun test() { ... } } 72 Kaspresso
  107. @RunWith(AndroidJUnit4::class) class OpenHomeScreenTest : TestCase( Kaspresso.Builder.default().apply { ... } )

    { private val mainScreen = MainScreen() private val homeScreen = HomeScreen() @get:Rule val activityTestRule = ActivityTestRule(MainActivity::class.java, true, false) @Test fun test() { ... } } 73 Kaspresso
  108. @RunWith(AndroidJUnit4::class) class OpenHomeScreenTest : TestCase( Kaspresso.Builder.default().apply { viewBehaviorInterceptors = mutableListOf()

    } ) { private val mainScreen = MainScreen() private val homeScreen = HomeScreen() @get:Rule val activityTestRule = ActivityTestRule(MainActivity::class.java, true, false) @Test fun test() { ... } } 74 Kaspresso
  109. @RunWith(AndroidJUnit4::class) class OpenHomeScreenTest : TestCase( Kaspresso.Builder.default().apply { viewBehaviorInterceptors.add(MyInterceptor()) } )

    { private val mainScreen = MainScreen() private val homeScreen = HomeScreen() @get:Rule val activityTestRule = ActivityTestRule(MainActivity::class.java, true, false) @Test fun test() { ... } } 75 Kaspresso
  110. @RunWith(AndroidJUnit4::class) class OpenHomeScreenTest : TestCase( Kaspresso.Builder.default().apply { viewBehaviorInterceptors.add(MyInterceptor()) flakySafetyParams.timeoutMs =

    1_000 } ) { private val mainScreen = MainScreen() private val homeScreen = HomeScreen() @get:Rule val activityTestRule = ActivityTestRule(MainActivity::class.java, true, false) @Test fun test() { ... } } 76 Kaspresso
  111. abstract class TestCase( kaspressoBuilder: Kaspresso.Builder ) { internal val kaspresso:

    Kaspresso = kaspressoBuilder.build() ... } Kaspresso initialization 77
  112. data class Kaspresso( internal val libLogger: UiTestLogger, internal val testLogger:

    UiTestLogger, internal val device: Device, internal val adbServer: AdbServer, internal val flakySafetyParams: FlakySafetyParams, internal val autoScrollParams: AutoScrollParams, internal val viewActionWatcherInterceptors: List<ViewActionWatcherInterceptor>, internal val viewAssertionWatcherInterceptors: List<ViewAssertionWatcherInterceptor>, internal val atomWatcherInterceptors: List<AtomWatcherInterceptor>, internal val webAssertionWatcherInterceptors: List<WebAssertionWatcherInterceptor>, internal val viewBehaviorInterceptors: List<ViewBehaviorInterceptor>, internal val dataBehaviorInterceptors: List<DataBehaviorInterceptor>, internal val webBehaviorInterceptors: List<BehaviorInterceptor>, internal val stepInterceptors: List<StepInterceptor>, internal val testRunInterceptors: List<TestRunInterceptor> ) Kaspresso configuration 78
  113. data class Kaspresso( internal val libLogger: UiTestLogger, internal val testLogger:

    UiTestLogger, internal val device: Device, internal val adbServer: AdbServer, internal val flakySafetyParams: FlakySafetyParams, internal val autoScrollParams: AutoScrollParams, internal val viewActionWatcherInterceptors: List<ViewActionWatcherInterceptor>, internal val viewAssertionWatcherInterceptors: List<ViewAssertionWatcherInterceptor>, internal val atomWatcherInterceptors: List<AtomWatcherInterceptor>, internal val webAssertionWatcherInterceptors: List<WebAssertionWatcherInterceptor>, internal val viewBehaviorInterceptors: List<ViewBehaviorInterceptor>, internal val dataBehaviorInterceptors: List<DataBehaviorInterceptor>, internal val webBehaviorInterceptors: List<BehaviorInterceptor>, internal val stepInterceptors: List<StepInterceptor>, internal val testRunInterceptors: List<TestRunInterceptor> ) Kaspresso configuration 78
  114. data class Kaspresso( internal val libLogger: UiTestLogger, internal val testLogger:

    UiTestLogger, internal val device: Device, internal val adbServer: AdbServer, internal val flakySafetyParams: FlakySafetyParams, internal val autoScrollParams: AutoScrollParams, internal val viewActionWatcherInterceptors: List<ViewActionWatcherInterceptor>, internal val viewAssertionWatcherInterceptors: List<ViewAssertionWatcherInterceptor>, internal val atomWatcherInterceptors: List<AtomWatcherInterceptor>, internal val webAssertionWatcherInterceptors: List<WebAssertionWatcherInterceptor>, internal val viewBehaviorInterceptors: List<ViewBehaviorInterceptor>, internal val dataBehaviorInterceptors: List<DataBehaviorInterceptor>, internal val webBehaviorInterceptors: List<BehaviorInterceptor>, internal val stepInterceptors: List<StepInterceptor>, internal val testRunInterceptors: List<TestRunInterceptor> ) Kaspresso configuration 78
  115. data class Kaspresso( internal val libLogger: UiTestLogger, internal val testLogger:

    UiTestLogger, internal val device: Device, internal val adbServer: AdbServer, internal val flakySafetyParams: FlakySafetyParams, internal val autoScrollParams: AutoScrollParams, internal val viewActionWatcherInterceptors: List<ViewActionWatcherInterceptor>, internal val viewAssertionWatcherInterceptors: List<ViewAssertionWatcherInterceptor>, internal val atomWatcherInterceptors: List<AtomWatcherInterceptor>, internal val webAssertionWatcherInterceptors: List<WebAssertionWatcherInterceptor>, internal val viewBehaviorInterceptors: List<ViewBehaviorInterceptor>, internal val dataBehaviorInterceptors: List<DataBehaviorInterceptor>, internal val webBehaviorInterceptors: List<BehaviorInterceptor>, internal val stepInterceptors: List<StepInterceptor>, internal val testRunInterceptors: List<TestRunInterceptor> ) Kaspresso configuration 78
  116. @RunWith(AndroidJUnit4::class) class OpenHomeScreenTest: TestCase() { private val mainScreen = MainScreen()

    private val homeScreen = HomeScreen() @get:Rule val activityTestRule = ActivityTestRule(MainActivity::class.java, true, false) @Test fun test() { ... } } 79
  117. @RunWith(AndroidJUnit4::class) class OpenHomeScreenTest: TestCase() { @Test fun test() { MainScreen()

    { homeButton.click() } HomeScreen() { title { isVisible() hasAnyText() } } } } 80
  118. @RunWith(AndroidJUnit4::class) class OpenHomeScreenTest: TestCase() { @Test fun test() { //1st

    step MainScreen() { homeButton.click() } //2nd step HomeScreen() { title { isVisible() hasAnyText() } } } } 81
  119. @RunWith(AndroidJUnit4::class) class OpenHomeScreenTest: TestCase() { @Test fun test() { //1st

    step Log.i(“1 step started”) MainScreen() { homeButton.click() } Log.i(“1st step succeeded”) //2nd step Log.i(“2nd step started”) HomeScreen() { title { isVisible() hasAnyText() } } Log.i(“2nd step succeeded”) } } 82
  120. @RunWith(AndroidJUnit4::class) class OpenHomeScreenTest: TestCase() { @Test fun test() { //1st

    step try { Log.i(“1st step started”) MainScreen() { homeButton.click() } Log.i(“1st step succeeded”) } catch(e: Throwable) { Log.i(“1st step failed”) takeScreenshot() throw e } //2nd step ... } } 83
  121. @RunWith(AndroidJUnit4::class) class OpenHomeScreenTest: TestCase() { @Test fun test() { step(“1.

    Open Home screen”) { MainScreen() { homeButton.click() } } step(“2. Check Home title”) { HomeScreen() { title { isVisible() hasAnyText() } } } } } 84
  122. @RunWith(AndroidJUnit4::class) class OpenHomeScreenTest: TestCase() { @Test fun test() { step("1.

    Open Home screen") { ... } step("2. Check Home title") { ... } step("3. Do some stuff") { ... } step("4. Check some stuff is done") { ... } step("5. Do another stuff") { ... } step("6. Check another stuff is done") { ... } step("7. 42") { ... } } } 85
  123. action.invoke() throw exception next step step success step fail interceptBefore()

    interceptFinally() interceptFailure() interceptSuccess() step start start success fail ➔ Logging ➔ Screenshots ➔ ... StepInterceptors 86
  124. data class Kaspresso( internal val libLogger: UiTestLogger, internal val testLogger:

    UiTestLogger, internal val device: Device, internal val adbServer: AdbServer, internal val flakySafetyParams: FlakySafetyParams, internal val autoScrollParams: AutoScrollParams, internal val viewActionWatcherInterceptors: List<ViewActionWatcherInterceptor>, internal val viewAssertionWatcherInterceptors: List<ViewAssertionWatcherInterceptor>, internal val atomWatcherInterceptors: List<AtomWatcherInterceptor>, internal val webAssertionWatcherInterceptors: List<WebAssertionWatcherInterceptor>, internal val viewBehaviorInterceptors: List<ViewBehaviorInterceptor>, internal val dataBehaviorInterceptors: List<DataBehaviorInterceptor>, internal val webBehaviorInterceptors: List<BehaviorInterceptor>, internal val stepInterceptors: List<StepInterceptor>, internal val testRunInterceptors: List<TestRunInterceptor> ) Kaspresso configuration 87
  125. I/KASPRESSO: ___________________________________________________________________________ I/KASPRESSO: TEST STEP: "1. Open Home screen" in

    OpenHomeScreenTest I/KASPRESSO: Check view has effective visibility=VISIBLE on AppCompatButton(id=activity_main_button_home;text=Home;) I/KASPRESSO: single click on AppCompatButton(id=activity_main_button_home;text=Home;) I/KASPRESSO: TEST STEP: "1. Open Home screen" in OpenHomeScreenTest SUCCEED. It took 0 minutes, 0 seconds and 618 millis. I/KASPRESSO: ___________________________________________________________________________ I/KASPRESSO: TEST STEP: "2. Check Home title" in OpenHomeScreenTest I/KASPRESSO: single click on AppCompatButton(id=button_1;text=Button 1;) I/KASPRESSO: Check view has effective visibility=VISIBLE on AppCompatButton(id=button_2;text=Button 2;) I/KASPRESSO: TEST STEP: "2. Check Home title" in OpenHomeScreenTest SUCCEED. It took 0 minutes, 0 seconds and 301 millis. I/KASPRESSO: ___________________________________________________________________________ I/KASPRESSO: TEST STEP: "3. Do some stuff" in OpenHomeScreenTest I/KASPRESSO: single click on AppCompatButton(id=button_2;text=Button 2;) I/KASPRESSO: Check view has effective visibility=VISIBLE on AppCompatEditText(id=edit;text=Some text;) I/KASPRESSO: Check view has effective visibility=VISIBLE on AppCompatEditText(id=edit;text=Some text;) I/KASPRESSO: Check with string from resource id: <2131558461> on AppCompatEditText(id=edit;text=Some text;) I/KASPRESSO: TEST STEP: "3. Do some stuff" in OpenHomeScreenTest SUCCEED. It took 0 minutes, 2 seconds and 138 millis. I/KASPRESSO: ___________________________________________________________________________ 88
  126. I/KASPRESSO: _____________________________________________________________ I/KASPRESSO: TEST STEP: "1. Open Home screen" in

    OpenHomeScreenTest I/KASPRESSO: Check view has effective visibility=VISIBLE on AppCompatButton(id=activity_main_button_home;text=Home;) I/KASPRESSO: single click on AppCompatButton(id=activity_main_button_home;text=Home;) I/KASPRESSO: TEST STEP: "1. Open Home screen" in OpenHomeScreenTest SUCCEED. It took 0 minutes, 0 seconds and 618 millis. I/KASPRESSO: ______________________________________________________________ I/KASPRESSO: TEST STEP: "2. Check Home title" in OpenHomeScreenTest I/KASPRESSO: single click on AppCompatButton(id=button_1;text=Button 1;) I/KASPRESSO: Check view has effective visibility=VISIBLE on AppCompatButton(id=button_2;text=Button 2;) I/KASPRESSO: TEST STEP: "2. Check Home title" in OpenHomeScreenTest SUCCEED. It took 0 minutes, 0 seconds and 301 millis. I/KASPRESSO: ___________________________________________________________________________ I/KASPRESSO: TEST STEP: "3. Do some stuff" in OpenHomeScreenTest I/KASPRESSO: single click on AppCompatButton(id=button_2;text=Button 2;) I/KASPRESSO: Check view has effective visibility=VISIBLE on AppCompatEditText(id=edit;text=Some text;) I/KASPRESSO: Check view has effective visibility=VISIBLE on AppCompatEditText(id=edit;text=Some text;) I/KASPRESSO: Check with string from resource id: <2131558461> on AppCompatEditText(id=edit;text=Some text;) I/KASPRESSO: TEST STEP: "3. Do some stuff" in OpenHomeScreenTest SUCCEED. It took 0 minutes, 2 seconds and 138 millis. I/KASPRESSO: ___________________________________________________________________________ 89
  127. I/KASPRESSO: _____________________________________________________________ I/KASPRESSO: TEST STEP: "1. Open Home screen" in

    OpenHomeScreenTest I/KASPRESSO: Check view has effective visibility=VISIBLE on AppCompatButton(id=activity_main_button_home;text=Home;) I/KASPRESSO: single click on AppCompatButton(id=activity_main_button_home;text=Home;) E/KASPRESSO: Failed to interact with view matching: (with id: actibity_main_button_back) because of AssertionFailedError I/KASPRESSO: TEST STEP: "1. Open Home screen" in OpenHomeScreenTest FAILED. It took 0 minutes, 0 seconds and 618 millis. I/KASPRESSO: ______________________________________________________________ 90
  128. I/KASPRESSO: _____________________________________________________________ I/KASPRESSO: TEST STEP: "1. Open Home screen" in

    OpenHomeScreenTest I/KASPRESSO: Check view has effective visibility=VISIBLE on AppCompatButton(id=activity_main_button_home;text=Home;) I/KASPRESSO: single click on AppCompatButton(id=activity_main_button_home;text=Home;) E/KASPRESSO: Failed to interact with view matching: (with id: actibity_main_button_back) because of AssertionFailedError I/KASPRESSO: TEST STEP: "1. Open Home screen" in OpenHomeScreenTest FAILED. It took 0 minutes, 0 seconds and 618 millis. I/KASPRESSO: ______________________________________________________________ 91 sdcard/screenshots OpenHomeScreenTest_step_1_failure Sign in
  129. @Test fun test() { step("Pass first run wizzard") { scenario(

    FirstRunWizzardScenario() ) step("Substep #1") { ... } step("Substep #2") { step("Inner substep #3") { ... } } } } 92
  130. @Test fun test() { step("Pass first run wizzard") { scenario(

    FirstRunWizzardScenario() ) step("Substep #1") { ... } step("Substep #2") { step("Inner substep #3") { ... } } } } 92
  131. @Test fun test() { step("Pass first run wizzard") { scenario(

    FirstRunWizzardScenario() ) step("Substep #1") { ... } step("Substep #2") { step("Inner substep #3") { ... } } } } 92
  132. @Test fun test() { step("Pass first run wizzard") { scenario(

    FirstRunWizzardScenario() ) step("Substep #1") { ... } step("Substep #2") { step("Inner substep #3") { ... } } } } class FirstRunWizzardScenario : Scenario() { override val steps: TestContext<Unit>.() -> Unit = { step("Check first wizzard screen") { ... } step("Check second wizzard screen") { ... } } } 92
  133. @RunWith(AndroidJUnit4::class) class OpenHomeScreenTest: TestCase() { @Test fun test() { step("1.

    Open Home screen") { ... } step("2. Check Home title") { ... } step("3. Do some stuff") { ... } step("4. Check some stuff is done") { ... } step("5. Do another stuff") { ... } step("6. Check another stuff is done") { ... } step("7. 42") { ... } } } 93
  134. @RunWith(AndroidJUnit4::class) class OpenHomeScreenTest: TestCase() { @Test fun test() { step("0.

    Turn off wifi") { ... } step("1. Open Home screen") { ... } step("2. Check Home title") { ... } step("3. Do some stuff") { ... } step("4. Check some stuff is done") { ... } step("5. Do another stuff") { ... } step("6. Check another stuff is done") { ... } step("7. 42") { ... } } } 94
  135. @RunWith(AndroidJUnit4::class) class OpenHomeScreenTest: TestCase() { @Test fun test() { step("0.

    Turn off wifi") { ... } step("1. Open Home screen") { ... } step("2. Check Home title") { ... } step("3. Do some stuff") { ... } step("4. Check some stuff is done") { ... } step("5. Do another stuff") { ... } step("6. Check another stuff is done") { ... } step("7. 42") { ... } step("8. Turn on wifi") { ... } } } 95
  136. @RunWith(AndroidJUnit4::class) class OpenHomeScreenTest: TestCase() { @Test fun test() { before

    { turnOffWiFi() }.after { turnOnWiFi() }.run { step("1. Open Home screen") { ... } step("2. Check Home title") { ... } step("3. Do some stuff") { ... } step("4. Check some stuff is done") { ... } step("5. Do another stuff") { ... } step("6. Check another stuff is done") { ... } step("7. 42") { ... } } } } 96
  137. @RunWith(AndroidJUnit4::class) class OpenHomeScreenTest: TestCase() { @Test fun test() { before

    { device.network.toggleWiFi(enable = false) }.after { device.network.toggleWiFi(enable = true) }.run { step("1. Open Home screen") { ... } step("2. Check Home title") { ... } step("3. Do some stuff") { ... } step("4. Check some stuff is done") { ... } step("5. Do another stuff") { ... } step("6. Check another stuff is done") { ... } step("7. 42") { ... } } } } 97
  138. ➔ device ➔ data ➔ testLogger ➔ adbServer ➔ flakySafely

    ➔ compose Test context 98
  139. ➔ device ➔ data ➔ testLogger ➔ adbServer ➔ flakySafely

    ➔ compose Test context device.files.push() device.phone.emulateCall(NUMBER) device.location.enableGps() device.exploit.pressBack() device.screenshots.take(TAG) 99
  140. ➔ device ➔ data ➔ testLogger ➔ adbServer ➔ flakySafely

    ➔ compose Test context 100
  141. @RunWith(AndroidJUnit4::class) class OpenHomeScreenTest: TestCase() { @Test fun test() { before

    { ... }.after { ... }.run { step("1. Open Home screen") { ... } step("2. Check Home title") { ... } step("3. Do some stuff") { ... } step("4. Check some stuff is done") { ... } step("5. Do another stuff") { ... } step("6. Check another stuff is done") { ... } step("7. 42") { ... } } } } 101
  142. @RunWith(AndroidJUnit4::class) class OpenHomeScreenTest: TestCase() { @Test fun test() { before

    { ... }.after { ... } .init { company { name = "Microsoft"; city = "Redmond" } company { name = "Google"; city = "Mountain View" } owner { firstName = "Satya"; secondName = "Nadella" } owner { firstName = "Sundar"; secondName = "Pichai" } } .run { step("1. Open Home screen") { ... } step("2. Check Home title") { ... } ... } } } 102
  143. @RunWith(AndroidJUnit4::class) class OpenHomeScreenTest: TestCase() { @Test fun test() { before

    { ... }.after { ... } .init { company { name = "Microsoft"; city = "Redmond" } company { name = "Google"; city = "Mountain View" } owner { firstName = "Satya"; secondName = "Nadella" } owner { firstName = "Sundar"; secondName = "Pichai" } }.transform { bind(ownerSurname = "Nadella", companyName = "Microsoft") bind(ownerSurname = "Pichai", companyName = "Google") } .run { step("1. Open Home screen") { ... } step("2. Check Home title") { ... } ... } } } 103
  144. @RunWith(AndroidJUnit4::class) class OpenHomeScreenTest: TestCase() { @Test fun test() { before

    { ... }.after { ... } .init { company { name = "Microsoft"; city = "Redmond" } company { name = "Google"; city = "Mountain View" } owner { firstName = "Satya"; secondName = "Nadella" } owner { firstName = "Sundar"; secondName = "Pichai" } }.transform { bind(ownerSurname = "Nadella", companyName = "Microsoft") bind(ownerSurname = "Pichai", companyName = "Google") } .run { step("1. Check description") { MainScreen().descriptionTextView { hasText(data.owners.first().firstName ?: "") } } } } } 104
  145. ➔ device ➔ data ➔ testLogger ➔ adbServer ➔ flakySafely

    ➔ compose Test context 105
  146. ➔ device ➔ data ➔ testLogger ➔ adbServer ➔ flakySafely

    ➔ compose Test context 105
  147. @RunWith(AndroidJUnit4::class) class OpenHomeScreenTest: TestCase() { @Test fun test() { before

    { ... }.after { ... } .init { ... }.transform { ...} .run { step("1. Open Home screen") { testLogger.i("I am test logger") MainScreen() { homeButton.click() } } step("2. Check Home title") { ... } step("3. Do some stuff") { ... } } } } 106
  148. I/KASPRESSO: _____________________________________________________________ I/KASPRESSO: TEST STEP: "1. Open Home screen" in

    OpenHomeScreenTest I/KASPRESSO_SPECIAL: I am test logger I/KASPRESSO: Check view has effective visibility=VISIBLE on AppCompatButton(id=activity_main_button_home;text=Home;) I/KASPRESSO: single click on AppCompatButton(id=activity_main_button_home;text=Home;) I/KASPRESSO: TEST STEP: "1. Open Home screen" in OpenHomeScreenTest SUCCEED. It took 0 minutes, 0 seconds and 618 millis. I/KASPRESSO: ______________________________________________________________ I/KASPRESSO: TEST STEP: "2. Check Home title" in OpenHomeScreenTest I/KASPRESSO: single click on AppCompatButton(id=button_1;text=Button 1;) I/KASPRESSO: Check view has effective visibility=VISIBLE on AppCompatButton(id=button_2;text=Button 2;) I/KASPRESSO: TEST STEP: "2. Check Home title" in OpenHomeScreenTest SUCCEED. It took 0 minutes, 0 seconds and 301 millis. I/KASPRESSO: ___________________________________________________________________________ I/KASPRESSO: TEST STEP: "3. Do some stuff" in OpenHomeScreenTest I/KASPRESSO: single click on AppCompatButton(id=button_2;text=Button 2;) I/KASPRESSO: Check view has effective visibility=VISIBLE on AppCompatEditText(id=edit;text=Some text;) I/KASPRESSO: Check view has effective visibility=VISIBLE on AppCompatEditText(id=edit;text=Some text;) I/KASPRESSO: Check with string from resource id: <2131558461> on AppCompatEditText(id=edit;text=Some text;) I/KASPRESSO: TEST STEP: "3. Do some stuff" in OpenHomeScreenTest SUCCEED. It took 0 minutes, 2 seconds and 138 millis. I/KASPRESSO: ___________________________________________________________________________ 107
  149. ➔ device ➔ data ➔ testLogger ➔ adbServer ➔ flakySafely

    ➔ compose Test context 108
  150. @RunWith(AndroidJUnit4::class) class OpenHomeScreenTest: TestCase() { @Test fun test() { before

    { ... }.after { ... } .run { step("1. Open Home screen") { MainScreen() { homeButton.click() } } step("2. Check Home title") { ... } step("3. Do some stuff") { ... } } } } 109
  151. @RunWith(AndroidJUnit4::class) class OpenHomeScreenTest: TestCase() { @Test fun test() { before

    { ... }.after { ... } .run { step("1. Open Home screen") { adbServer.performAdb( "adb push foo.txt /sdcard/foo.txt" ) MainScreen() { homeButton.click() } } step("2. Check Home title") { ... } step("3. Do some stuff") { ... } } } } 110
  152. ➔ device ➔ data ➔ testLogger ➔ adbServer ➔ flakySafely

    ➔ compose Test context 111
  153. @RunWith(AndroidJUnit4::class) class OpenHomeScreenTest: TestCase() { @Test fun test() { before

    { ... }.after { ... } .run { step("1. Open Home screen") { MainScreen() { flakySafely(timeoutMs = 7_000) { homeButton.click() } } } step("2. Check Home title") { ... } step("3. Do some stuff") { ... } } } } 112
  154. ➔ device ➔ data ➔ testLogger ➔ adbServer ➔ flakySafely

    ➔ compose Test context 113
  155. @RunWith(AndroidJUnit4::class) class OpenHomeScreenTest: TestCase() { @Test fun test() { before

    { ... }.after { ... } .run { step("1. Open Home screen") { MainScreen() { compose { or(redBtn) { hasText("blue") } or(blueBtn) { hasText("blue") } } homeButton.click() } } step("2. Check Home title") { ... } step("3. Do some stuff") { ... } } } } 114
  156. @RunWith(AndroidJUnit4::class) class OpenHomeScreenTest: TestCase() { @Test fun test() { before

    { ... }.after { ... } .run { step("1. Open Home screen") { MainScreen() { redBtn.compose { or { hasText("blue") } or { hasText("red") } } homeButton.click() } } step("2. Check Home title") { ... } step("3. Do some stuff") { ... } } } } 115
  157. @RunWith(AndroidJUnit4::class) class OpenHomeScreenTest: TestCase() { @Test fun test() { before

    { ... } .after { ... } .init { ... } .transform { ...} .run { step("1. Open Home screen") { ... } step("2. Check Home title") { ... } step("3. Do some stuff") { ... } } } } 116
  158. step #2 step #1 step #3 run before after step

    #2 Test runner Test report success fail success fail success fail TestRunInterceptors ➔ Logging ➔ Screenshots ➔ ... start start start start success fail 117
  159. data class Kaspresso( internal val libLogger: UiTestLogger, internal val testLogger:

    UiTestLogger, internal val device: Device, internal val adbServer: AdbServer, internal val flakySafetyParams: FlakySafetyParams, internal val autoScrollParams: AutoScrollParams, internal val viewActionWatcherInterceptors: List<ViewActionWatcherInterceptor>, internal val viewAssertionWatcherInterceptors: List<ViewAssertionWatcherInterceptor>, internal val atomWatcherInterceptors: List<AtomWatcherInterceptor>, internal val webAssertionWatcherInterceptors: List<WebAssertionWatcherInterceptor>, internal val viewBehaviorInterceptors: List<ViewBehaviorInterceptor>, internal val dataBehaviorInterceptors: List<DataBehaviorInterceptor>, internal val webBehaviorInterceptors: List<BehaviorInterceptor>, internal val stepInterceptors: List<StepInterceptor>, internal val testRunInterceptors: List<TestRunInterceptor> ) Kaspresso configuration 118
  160. I/KASPRESSO: ---------------------------------------------------------- I/KASPRESSO: BEFORE TEST SECTION I/KASPRESSO: ---------------------------------------------------------- I/KASPRESSO: command=shell

    svc data disable was performed with result=CommandResult(status=SUCCESS) I/KASPRESSO: ---------------------------------------------------------- I/KASPRESSO: TEST SECTION I/KASPRESSO: ---------------------------------------------------------- I/KASPRESSO: __________________________________________________________ I/KASPRESSO: TEST STEP: "1. Open Home Screen" in OpenHomeScreenTest I/KASPRESSO: Check view has effective visibility=VISIBLE on AppCompatButton(id=home_btn;text=Home;) I/KASPRESSO: single click on AppCompatButton(id=home_btn;text=Home;) I/KASPRESSO: TEST STEP: "1. Open Home screen" in OpenHomeScreenTest SUCCEED. It took 0 minutes, 0 seconds and 618 millis. I/KASPRESSO: __________________________________________________________ I/KASPRESSO: __________________________________________________________ I/KASPRESSO: TEST STEP: "2. Check Home title" in OpenHomeScreenTest I/KASPRESSO: type text(111) on AppCompatEditText(id=edit;) I/KASPRESSO: Check with text: is "111" on AppCompatEditText(id=edit;text=111;) I/KASPRESSO: TEST STEP: "2. Check Home title" in OpenHomeScreenTest SUCCEED. It took 0 minutes, 0 seconds and 621 millis. I/KASPRESSO: __________________________________________________________ I/KASPRESSO: ---------------------------------------------------------- I/KASPRESSO: AFTER TEST SECTION I/KASPRESSO: ---------------------------------------------------------- I/KASPRESSO: command=shell svc data enable was performed with result=CommandResult(status=SUCCESS) I/KASPRESSO: ---------------------------------------------------------- I/KASPRESSO: TEST PASSED I/KASPRESSO: ---------------------------------------------------------- Sign in screenshots saved at sdcard/screenshots 119
  161. I/KASPRESSO: --------------------------------------------- I/KASPRESSO: BEFORE TEST SECTION I/KASPRESSO: --------------------------------------------- I/KASPRESSO: command=shell

    svc data disable was performed with result=CommandResult(status=SUCCESS) I/KASPRESSO: --------------------------------------------- I/KASPRESSO: TEST SECTION I/KASPRESSO: ---------------------------------------------------------- I/KASPRESSO: __________________________________________________________ I/KASPRESSO: TEST STEP: "1. Open Home Screen" in OpenHomeScreenTest I/KASPRESSO: Check view has effective visibility=VISIBLE on AppCompatButton(id=home_btn;text=Home;) I/KASPRESSO: single click on AppCompatButton(id=home_btn;text=Home;) I/KASPRESSO: TEST STEP: "1. Open Home screen" in OpenHomeScreenTest SUCCEED. It took 0 minutes, 0 seconds and 618 millis. I/KASPRESSO: __________________________________________________________ I/KASPRESSO: __________________________________________________________ I/KASPRESSO: TEST STEP: "2. Check Home title" in OpenHomeScreenTest I/KASPRESSO: type text(111) on AppCompatEditText(id=edit;) I/KASPRESSO: Check with text: is "111" on AppCompatEditText(id=edit;text=111;) I/KASPRESSO: TEST STEP: "2. Check Home title" in OpenHomeScreenTest SUCCEED. It took 0 minutes, 0 seconds and 621 millis. I/KASPRESSO: __________________________________________________________ I/KASPRESSO: ---------------------------------------------------------- I/KASPRESSO: AFTER TEST SECTION I/KASPRESSO: ---------------------------------------------------------- I/KASPRESSO: command=shell svc data enable was performed with result=CommandResult(status=SUCCESS) I/KASPRESSO: ---------------------------------------------------------- I/KASPRESSO: TEST PASSED I/KASPRESSO: ---------------------------------------------------------- Sign in screenshots saved at sdcard/screenshots 120
  162. I/KASPRESSO: ---------------------------------------------------------- I/KASPRESSO: BEFORE TEST SECTION I/KASPRESSO: ---------------------------------------------------------- I/KASPRESSO: command=shell

    svc data disable was performed with result=CommandResult(status=SUCCESS) I/KASPRESSO: --------------------------------------------- I/KASPRESSO: TEST SECTION I/KASPRESSO: --------------------------------------------- I/KASPRESSO: __________________________________________________________ I/KASPRESSO: TEST STEP: "1. Open Home Screen" in OpenHomeScreenTest I/KASPRESSO: Check view has effective visibility=VISIBLE on AppCompatButton(id=home_btn;text=Home;) I/KASPRESSO: single click on AppCompatButton(id=home_btn;text=Home;) I/KASPRESSO: TEST STEP: "1. Open Home screen" in OpenHomeScreenTest SUCCEED. It took 0 minutes, 0 seconds and 618 millis. I/KASPRESSO: __________________________________________________________ I/KASPRESSO: __________________________________________________________ I/KASPRESSO: TEST STEP: "2. Check Home title" in OpenHomeScreenTest I/KASPRESSO: type text(111) on AppCompatEditText(id=edit;) I/KASPRESSO: Check with text: is "111" on AppCompatEditText(id=edit;text=111;) I/KASPRESSO: TEST STEP: "2. Check Home title" in OpenHomeScreenTest SUCCEED. It took 0 minutes, 0 seconds and 621 millis. I/KASPRESSO: __________________________________________________________ I/KASPRESSO: ---------------------------------------------------------- I/KASPRESSO: AFTER TEST SECTION I/KASPRESSO: ---------------------------------------------------------- I/KASPRESSO: command=shell svc data enable was performed with result=CommandResult(status=SUCCESS) I/KASPRESSO: ---------------------------------------------------------- I/KASPRESSO: TEST PASSED I/KASPRESSO: ---------------------------------------------------------- Sign in screenshots saved at sdcard/screenshots 121
  163. I/KASPRESSO: ---------------------------------------------------------- I/KASPRESSO: BEFORE TEST SECTION I/KASPRESSO: ---------------------------------------------------------- I/KASPRESSO: command=shell

    svc data disable was performed with result=CommandResult(status=SUCCESS) I/KASPRESSO: ---------------------------------------------------------- I/KASPRESSO: TEST SECTION I/KASPRESSO: ---------------------------------------------------------- I/KASPRESSO: __________________________________________________________ I/KASPRESSO: TEST STEP: "1. Open Home Screen" in OpenHomeScreenTest I/KASPRESSO: Check view has effective visibility=VISIBLE on AppCompatButton(id=home_btn;text=Home;) I/KASPRESSO: single click on AppCompatButton(id=home_btn;text=Home;) I/KASPRESSO: TEST STEP: "1. Open Home screen" in OpenHomeScreenTest SUCCEED. It took 0 minutes, 0 seconds and 618 millis. I/KASPRESSO: __________________________________________________________ I/KASPRESSO: __________________________________________________________ I/KASPRESSO: TEST STEP: "2. Check Home title" in OpenHomeScreenTest I/KASPRESSO: type text(111) on AppCompatEditText(id=edit;) I/KASPRESSO: Check with text: is "111" on AppCompatEditText(id=edit;text=111;) I/KASPRESSO: TEST STEP: "2. Check Home title" in OpenHomeScreenTest SUCCEED. It took 0 minutes, 0 seconds and 621 millis. I/KASPRESSO: __________________________________________________________ I/KASPRESSO: --------------------------------------------- I/KASPRESSO: AFTER TEST SECTION I/KASPRESSO: --------------------------------------------- I/KASPRESSO: command=shell svc data enable was performed with result=CommandResult(status=SUCCESS) I/KASPRESSO: --------------------------------------------- I/KASPRESSO: TEST PASSED I/KASPRESSO: ---------------------------------------------------------- Sign in screenshots saved at sdcard/screenshots 122
  164. I/KASPRESSO: ---------------------------------------------------------- I/KASPRESSO: BEFORE TEST SECTION I/KASPRESSO: ---------------------------------------------------------- I/KASPRESSO: command=shell

    svc data disable was performed with result=CommandResult(status=SUCCESS) I/KASPRESSO: ---------------------------------------------------------- I/KASPRESSO: TEST SECTION I/KASPRESSO: ---------------------------------------------------------- I/KASPRESSO: __________________________________________________________ I/KASPRESSO: TEST STEP: "1. Open Home Screen" in OpenHomeScreenTest I/KASPRESSO: Check view has effective visibility=VISIBLE on AppCompatButton(id=home_btn;text=Home;) I/KASPRESSO: single click on AppCompatButton(id=home_btn;text=Home;) I/KASPRESSO: TEST STEP: "1. Open Home screen" in OpenHomeScreenTest SUCCEED. It took 0 minutes, 0 seconds and 618 millis. I/KASPRESSO: __________________________________________________________ I/KASPRESSO: __________________________________________________________ I/KASPRESSO: TEST STEP: "2. Check Home title" in OpenHomeScreenTest I/KASPRESSO: type text(111) on AppCompatEditText(id=edit;) I/KASPRESSO: Check with text: is "111" on AppCompatEditText(id=edit;text=111;) I/KASPRESSO: TEST STEP: "2. Check Home title" in OpenHomeScreenTest SUCCEED. It took 0 minutes, 0 seconds and 621 millis. I/KASPRESSO: __________________________________________________________ I/KASPRESSO: ---------------------------------------------------------- I/KASPRESSO: AFTER TEST SECTION I/KASPRESSO: ---------------------------------------------------------- I/KASPRESSO: command=shell svc data enable was performed with result=CommandResult(status=SUCCESS) I/KASPRESSO: --------------------------------------------- I/KASPRESSO: TEST PASSED I/KASPRESSO: --------------------------------------------- Sign in screenshots saved at sdcard/screenshots 123
  165. I/KASPRESSO: --------------------------------------------- I/KASPRESSO: BEFORE TEST SECTION I/KASPRESSO: --------------------------------------------- I/KASPRESSO: command=shell

    svc data disable was performed with result=CommandResult(status=FAILED) I/KASPRESSO: --------------------------------------------- I/KASPRESSO: BEFORE TEST SECTION FAILED I/KASPRESSO: --------------------------------------------- I/KASPRESSO: ---------------------------------------------------------- I/KASPRESSO: AFTER TEST SECTION I/KASPRESSO: ---------------------------------------------------------- I/KASPRESSO: command=shell svc data enable was performed with result=CommandResult(status=SUCCESS) I/KASPRESSO: ---------------------------------------------------------- I/KASPRESSO: TEST FAILED I/KASPRESSO: ---------------------------------------------------------- Sign in screenshots saved at sdcard/screenshots 124
  166. I/KASPRESSO: ---------------------------------------------------------- I/KASPRESSO: BEFORE TEST SECTION I/KASPRESSO: ---------------------------------------------------------- I/KASPRESSO: command=shell

    svc data disable was performed with result=CommandResult(status=FAILED) I/KASPRESSO: ---------------------------------------------------------- I/KASPRESSO: --------------------------------------------- I/KASPRESSO: AFTER TEST SECTION I/KASPRESSO: --------------------------------------------- I/KASPRESSO: command=shell svc data enable was performed with result=CommandResult(status=SUCCESS) I/KASPRESSO: --------------------------------------------- I/KASPRESSO: TEST FAILED I/KASPRESSO: --------------------------------------------- Sign in screenshots saved at sdcard/screenshots 125
  167. ➔ Читаемость Kakao Kaspresso 126

  168. ➔ Читаемость Kakao ➔ Реализует flaky safety Kaspresso 126

  169. ➔ Читаемость Kakao ➔ Реализует flaky safety ➔ Предоставляет удобный

    механизм логирования Kaspresso 126
  170. ➔ Читаемость Kakao ➔ Реализует flaky safety ➔ Предоставляет удобный

    механизм логирования ➔ Позволяет снимать скриншоты Kaspresso 126
  171. ➔ Читаемость Kakao ➔ Реализует flaky safety ➔ Предоставляет удобный

    механизм логирования ➔ Позволяет снимать скриншоты ➔ Предоставляет интерфейс взаимодействия с OS Android Kaspresso 126
  172. ➔ Читаемость Kakao ➔ Реализует flaky safety ➔ Предоставляет удобный

    механизм логирования ➔ Позволяет снимать скриншоты ➔ Предоставляет интерфейс взаимодействия с OS Android ➔ Умеет в ADB Kaspresso 126
  173. ➔ Читаемость Kakao ➔ Реализует flaky safety ➔ Предоставляет удобный

    механизм логирования ➔ Позволяет снимать скриншоты ➔ Предоставляет интерфейс взаимодействия с OS Android ➔ Умеет в ADB ➔ Предоставляет решение по архитектуре Kaspresso 126
  174. ➔ Читаемость Kakao ➔ Реализует flaky safety ➔ Предоставляет удобный

    механизм логирования ➔ Позволяет снимать скриншоты ➔ Предоставляет интерфейс взаимодействия с OS Android ➔ Умеет в ADB ➔ Предоставляет решение по архитектуре ➔ Гибко настраивается и конфигурируется Kaspresso 126
  175. ➔ Читаемость Kakao ➔ Реализует flaky safety ➔ Предоставляет удобный

    механизм логирования ➔ Позволяет снимать скриншоты ➔ Предоставляет интерфейс взаимодействия с OS Android ➔ Умеет в ADB ➔ Предоставляет решение по архитектуре ➔ Гибко настраивается и конфигурируется ➔ Может быть кастомизирован Kaspresso 126
  176. Our team Евгений Мацюк Андрей Антипов Егор Курников Дмитрий Мовчан

    Руслан Мингалиев Александр Блинов Алексей Творогов Николай Нестеров Павел Стрельченко Ринат Агишев 127 Дмитрий Воронин Сергей Занкин
  177. ➔ Kaspresso: https://github.com/KasperskyLab/Kaspresso ➔ AdbServer: https://github.com/KasperskyLab/AdbServer ➔ Чат поддержки: https://t.me/kaspresso

    Links 128
  178. ВНИМАНИЕ! Спасибо за внимание