Kaspresso Versions Save

Android UI test framework

v1.2.0

3 years ago

Autotest AdbServer

AdbServer is a part of Kaspresso

We've decided to merge AdbServer into main Kaspresso repository. AdbServer is very tied with Kaspresso and there is no mind to maintain two separate repositories. Further support and development should be easier.

New AdbServer logging

AdbServer logging is more enjoyable, suitable, and understandable now. Take a look at the example of logs from the Deskop:

INFO 10/09/2020 11:37:20.185  desktop=Desktop-25920 device=emulator-5554   message: The attempt to connect to Device was a success

You can see the type of a message, date and time, the desktop executing the message and the emulator giving the task, and the message. Another example of logs from the Device:

2020-09-10 12:24:27.427 10349-10378/com.kaspersky.kaspressample I/KASPRESSO_ADBSERVER: The result of command=AdbCommand(body=shell su 0 svc data disable) => CommandResult(status=SUCCESS, description=exitCode=0, message=, serviceInfo=The command was executed on desktop=Desktop-30548)

Here is very useful information about where the command was executed. In this example, the command was executed on Desktop-30548. You can choose an appropriate type of logging: VERBOSE, DEBUG, INFO, WARN, ERROR. INFO is a default type providing all basic logs. In some cases VERBOSE and DEBUG modes could be useful too.
Also, a lot of potential bugs and issues were fixed. All information is available in AdbServer wiki.

Fixes and improvements

  • Updated Network implementation is able to turn on/off network even without running AdbServer.
  • Improved compose with extended API.
  • Fixed Logcat implementation that required AdbServer to be run before every test.
  • StepInfo is available in every step now.
  • Added more flexible way to struct folders after screenshot tests.
  • Bumped library versions.
  • Improved samples stability.
  • Many small bugs has been fixed.

Breaking changes

  1. We've totally reworked AdbServer and Kaspresso 1.2.0 works only with new artifacts/adbserver-desktop.jar The old version artifacts/desktop_1_1_0.jar is also available for use with older versions of Kaspresso.
  2. If you use device.logcat in your tests, you should call device.logcat.disableChatty in the before section of your test. In previous version of Kaspresso, device.logcat.disableChatty was called automatically during initialization. This resulted in the need to always run AdbServer before tests.

v1.1.0

4 years ago

Wrapper over UI Automator: Kautomator

We are happy to announce a new unique Kotlin DSL wrapper over UI Automator: Kautomator! Let's consider the main features.

Speed

The most amazing feature of Kautomator is an incredible boost of UI Automator. Just have a look at the video below:
The left video is a boosted UI Automator, the right video is a default UI Automator. All details are available here.

Readability

Have a look at a typical piece of code written with UI Automator:

val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
val uiDevice = UiDevice.getInstance(instrumentation)

val uiObject = uiDevice.wait(
    Until.findObject(
        By.res(
            "com.kaspersky.kaspresso.sample_kautomator",
            "editText"
        )
    ),
    2_000
)

uiObject.text = "Kaspresso"
assertEquals(uiObject.text, "Kaspresso")

And just evaluate a Kautomator API that is doing the same thing:

MainScreen {
    simpleEditText {
        replaceText("Kaspresso")
        hasText("Kaspresso")
    }
}

You can find a comprehensive Kautomator tutorial here.

Flaky safe

By default, Kaspresso provides a rich set of interceptors for Kautomator that prevent flakiness of UI Automator. How does it work? You are welcome to read here and here.

Let's evaluate multi-stage protection from flakiness in UI Automator:

  1. Attempt to scroll a parent layout to make the View (UiObject2) visible.
  2. Attempt to reload UiObject2 by BySelector.
  3. Attempt to suppress android system dialogs if they exist.
  4. Attempt to repeat failed action for 10 seconds.

Conclusion

Forget your pain, forget your suffering. Just enjoy writing stable and fast tests even though these tests are using UI Automator under the hood.

What else?

Diff in 380 commits contains not only Kautomator =) Have a glance at some sweet features and changes.

Tests for upgrade scenarios are possible now

Yep, a developer/tester can automate upgrade scenarios using Kaspresso now. Check the example to learn how to do it.

GDPR check

If you have not heard about GDPR and high-profile lawsuits then you are lucky. But, if your application works in Europe then it's so important to enable/disable all analytics/statistics according to acceptance of the agreements. One of the most reliable ways to check analytics/statistics sending is to verify logcat where all analytics/statistics write their logs (in debug mode, sure). That's why we have created a special Logcat class providing a wide variety of ways to check logcat. Please, observe the example and save money of your company =)

Continuously

Kaspresso provides a new continuously method that can be useful for your tests. As opposed to flakySafely, it does not skip the last attempt after the first success and throws inner exception outside as soon as it was thrown. It can be especially helpful for checking negative scenarios.

continuously {
    dialogTitle {
        doesNotExist()
    }
}

Check the source code and example.

Compose

We've updated and extended compose functionality. The main changes are the UI Automator support and the keyword then adding:

compose {
    // the first potential branch when ComplexComposeScreen.stage1Button is visible
    // here, Espresso is used under the hood
    or(ComplexComposeScreen.stage1Button) {
        isVisible()
    } then {
        // if the first branch was succeed then we execute some special flow
        step("Flow is over the product") {
            ComplexComposeScreen {
                stage1Button {
                    click()
                }
                stage2Button {
                    isVisible()
                    click()
                }
            }
        }
    }
    // the second potential branch when UiComposeDialog1.title is visible
    // just imagine that is some unexpected system or product behavior and we cannot fix it now
    // here, UI Automator is used under the hood
    or(UiComposeDialog1.title) {
        isDisplayed()
    } then {
        // if the second branch was succeed then we execute some special flow
        step("Flow is over dialogs") {
            UiComposeDialog1 {
                okButton {
                    isDisplayed()
                    click()
                }
            }
            UiComposeDialog2 {
                title {
                    isDisplayed()
                }
                okButton {
                    isDisplayed()
                    click()
                }
            }
        }
    }
}

But remember, compose is useful in cases when we don't know an accurate sequence of events and can't influence it. Such cases are possible when a test is performed outside the application. When a test is performed inside the application we strongly recommend to make your test linear and not to put any conditions in tests that are possible thanks to compose. For more information, please check the documentation, the example and some additional possibilities.

Increased common timeouts

We have decided to increase all default timeouts from 2 seconds to 10 seconds. Usage of UI Automator has been the main reason for this decision.

BaseTestCase and BaseTestContext improvements

TestAssistantsProvider

We have introduced a special provider that is available via BaseTestCase and BaseTestContext:

interface TestAssistantsProvider {

    val device: Device
    val adbServer: AdbServer
    val testLogger: UiTestLogger
    val params: Params
}

If you have forgotten what BaseTestCase and BaseTestContext mean then let me remind you:

@RunWith(AndroidJUnit4::class)
class SimpleTest : TestCase() { <===== TestCase is an inheritor of BaseTestContext
                                                     <===== TestAssistantsProvider is available
    // ....

    @Test
    fun test() =
        before {  <===== a lambda with receiver where receiver is BaseTestContext
            // ....    <===== TestAssistantsProvider is available
        }.after {   <===== a lambda with receiver where receiver is BaseTestContext
            // ....    <===== TestAssistantsProvider is available
        }.run {     <===== a lambda with receiver where receiver is inheritor of BaseTestContext
            // ....    <===== TestAssistantsProvider is available
        }
}

It might be useful when a developer wants to put some common code into separate classes. But this common code interacts with mentioned properties like device, adbServer, testLogger, params`. So, your code refactoring will be easier.

beforeEachTest/afterEachTest

Sometimes, a developer wishes to put some actions repeating in all tests before/after into a single place to simplify the maintenance of tests. You can make a remark that there are @beforeTest/@afterTest annotations to resolve the mentioned tasks. But the developer doesn't have access to BaseTestContext in those methods. That's why we have introduced special default actions that you can set in the constructor by Kaspresso.Builder. The example of how to implement default actions in Kaspresso.Builder is:

open class YourTestCase : TestCase(
    kaspressoBuilder = Kaspresso.Builder.advanced {
        beforeEachTest {
            testLogger.i("beforeTestFirstAction")
            flakySafety { device.network.enable() }
            flakySafety { device.language.switchInApp(FRENCH) }
        }
        afterEachTest {
            testLogger.i("afterTestFirstAction")
        }
    }
)

The more detailed info is available here.

Enricher

A developer can also extend parametrized tests functionality by providing MainSectionEnricher in BaseTestCase or BaseTestCaseRule. The main idea of enrichers - allow adding additional test case's steps before and after the main section's run block. It looks like:

class LoggingMainSectionEnricher : MainSectionEnricher<TestCaseData> {

    override fun TestContext<TestCaseData>.beforeMainSectionRun(testInfo: TestInfo) {
        testLogger.d("Before main section run... | ${testInfo.testName}")
        step("Check users count...") {
            testLogger.d("Check users count: ${data.users.size}")
        }
    }

    override fun TestContext<TestCaseData>.afterMainSectionRun(testInfo: TestInfo) {
        testLogger.d("After main section run... | ${testInfo.testName}")
        step("Check posts count...") {
            testLogger.d("Check posts count: ${data.posts.size}")
        }
    }

}

class EnricherBaseTestCase : BaseTestCase<TestCaseDsl, TestCaseData>(
    kaspresso = Kaspresso.Builder.default(),
    dataProducer = { action -> TestCaseDataCreator.initData(action) },
    mainSectionEnrichers = listOf(
        LoggingMainSectionEnricher(),
        AnalyticsMainSectionEnricher()
    )
)

The documentation is here. The example is here.

Other improvements

  1. Bug fixes.
  2. Small improvements.
  3. Updated and improved wiki.
  4. Updated and improved example modules: sample, sample_kautomator, sample_upgrade_tests.
  5. Updated and improved README.

v1.0.1

4 years ago

Minor fixes for correct artifactory delivery

v1.0.0

4 years ago

Initial Release with the core functionality of Kaspresso. Please observe README file for additional info.