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

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

MOSDROID
September 13, 2019

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

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

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

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

MOSDROID

September 13, 2019
Tweet

More Decks by MOSDROID

Other Decks in Programming

Transcript

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

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

    Что делать если нужного инструмента нет? Зачем нам фреймворк? 5
  3. @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
  4. @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
  5. @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
  6. @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
  7. 9

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

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

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

    Работа с OS Android ➔ Архитектура Чего мы хотим? 16
  11. val button = KButton { withId(R.id.my_btn) } val textView =

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

    KTextView { ... } btn.click() textView { isVisible() hasText(R.string.title) } KView 20
  13. 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
  14. 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
  15. 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
  16. 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
  17. @Test fun test() { MainScreen() { nextButton { isVisible() click()

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

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

    Работа с OS Android ➔ Архитектура Чего мы хотим? 25
  20. @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
  21. @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
  22. @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
  23. @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
  24. @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
  25. @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
  26. @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
  27. 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
  28. ➔ Хорошая читаемость ➔ Стабильность ➔ Логирование ➔ Скриншоты ➔

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

    Работа с OS Android ➔ Архитектура Чего мы хотим? 38
  30. 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
  31. 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
  32. 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
  33. ➔ Хорошая читаемость ➔ Стабильность ➔ Логирование ➔ Скриншоты ➔

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

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

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

    ➔ Keyboard ➔ Location ➔ Network ➔ Permissions ➔ Phone ➔ Screenshots Device 47
  37. ➔ 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
  38. ➔ 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
  39. ➔ Accessibility ➔ Activities ➔ Apps ➔ Exploit ➔ Files

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

    системных настроек adb emu ➔ Установка геопозиции ➔ Выставление скорости сети, задержки ➔ Симуляция вызовов ➔ Выставление значений акселерометра ➔ Имитация работы с отпечатком пальца 51
  41. Virtual routing 10.0.2.2 - Special alias to your loopback interface

    (i.e. 127.0.0.1 on your development machine) 53
  42. $ 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
  43. $ 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
  44. Client app App adb forward tcp:6100 tcp:8500 Client localhost: 8500

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

    6100 Client localhost: 8500 Server localhost:8500
  46. 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
  47. 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
  48. ➔ Хорошая читаемость ➔ Стабильность ➔ Логирование ➔ Скриншоты ➔

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

    Работа с OS Android ➔ Архитектура Чего мы хотим? 66
  50. @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
  51. @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
  52. @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
  53. @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
  54. @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
  55. @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
  56. @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
  57. @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
  58. @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
  59. abstract class TestCase( kaspressoBuilder: Kaspresso.Builder ) { internal val kaspresso:

    Kaspresso = kaspressoBuilder.build() ... } Kaspresso initialization 77
  60. 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
  61. 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
  62. 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
  63. 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
  64. @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
  65. @RunWith(AndroidJUnit4::class) class OpenHomeScreenTest: TestCase() { @Test fun test() { MainScreen()

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

    step MainScreen() { homeButton.click() } //2nd step HomeScreen() { title { isVisible() hasAnyText() } } } } 81
  67. @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
  68. @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
  69. @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
  70. @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
  71. action.invoke() throw exception next step step success step fail interceptBefore()

    interceptFinally() interceptFailure() interceptSuccess() step start start success fail ➔ Logging ➔ Screenshots ➔ ... StepInterceptors 86
  72. 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
  73. 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
  74. 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
  75. 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
  76. 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
  77. @Test fun test() { step("Pass first run wizzard") { scenario(

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

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

    FirstRunWizzardScenario() ) step("Substep #1") { ... } step("Substep #2") { step("Inner substep #3") { ... } } } } 92
  80. @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
  81. @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
  82. @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
  83. @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
  84. @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
  85. @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
  86. ➔ 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
  87. @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
  88. @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
  89. @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
  90. @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
  91. @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
  92. 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
  93. @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
  94. @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
  95. @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
  96. @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
  97. @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
  98. @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
  99. 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
  100. 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
  101. 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
  102. 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
  103. 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
  104. 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
  105. 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
  106. 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
  107. 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
  108. ➔ Читаемость Kakao ➔ Реализует flaky safety ➔ Предоставляет удобный

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

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

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

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

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

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

    Руслан Мингалиев Александр Блинов Алексей Творогов Николай Нестеров Павел Стрельченко Ринат Агишев 127 Дмитрий Воронин Сергей Занкин