Skip to content

Kherkin is an Android UI testing framework that simplifies writing UI tests

License

Notifications You must be signed in to change notification settings

Progressive-Insurance/kherkin

Repository files navigation

Kherkin

Kherkin (Kotlin + Gherkin) is an instrumentation testing framework that simplifies writing UI tests for Android. The Gherkin-like syntax makes it easier to build the test scenarios as a collaboration between developers and non-developers. Predefined step definitions allow for writing tests without having to fiddle with the Espresso testing framework or the Jetpack Compose testing APIs. Tests can start from any activity without navigating the entire application.

Kherkin is written for both XML layouts and Jetpack Compose activities. It offers a dedicated library for each approach, allowing you the flexibility to include either one or both libraries in your project, based on your specific needs.

(Visit the sister project Swerkin for Swift/iOS!)

Getting Started

(For a working example, check out the sample app)

The first step is to add the Kherkin library or libraries to your project.

androidTestImplementation "com.progressive.kherkin:kherkin-espresso:<version>"
androidTestImplementation "com.progressive.kherkin:kherkin-compose:<version>"

Extend the BaseIntegrationTestCase for setup and tear down. See BaseIntegrationTestCase below for details.

open class SampleBaseIntegrationTestCase : BaseIntegrationTestCase() {

    @Before
    override fun setUp() {
        super.setUp()
        BaseIntegrationComponentHolder.component = BaseIntegrationComponent()
    }

    @After
    override fun tearDown() {
        val appContext: Context = ApplicationProvider.getApplicationContext()
        if (appContext is IntegrationTestCleanup) {
            appContext.cleanup()
        }
        super.tearDown()
    }
}

Extend the IntegrationSetupInterface to get the current screen.

class IntegrationSetup @Inject constructor() : IntegrationSetupInterface {
    override fun startActivity() {
        Screen.myCurrentScreen.startMyActivity()
    }

    override fun getStartScreen(): Screen {
        return Screen.myCurrentScreen
    }
}

class BaseIntegrationComponent : BaseIntegrationFrameworkComponent {
    override fun integrationSetup(): IntegrationSetupInterface {
        return IntegrationSetup()
    }
}

Create a screen object for your activity. See Screen Objects below for details.

class MainScreen : Screen(), Navigable {

    override lateinit var activityScenario: ActivityScenario<MainActivity>
    override val trait: Trait = Trait("Main Activity")
    override fun screenActivityClass(): Class<out Activity> = MainActivity::class.java

    override fun startMyActivity() {
        activityScenario = ActivityScenario.launch(MainActivity::class.java)
    }

    override fun pathsToScreen(): List<PathSegment> = emptyList()
}

Write a test!

XML based activity:

class TestMain : SampleBaseIntegrationTestCase() {
    @Test
    fun testMainScreenDisplays() {
        Given.IRenderScreen(MainScreen())
        When.IWaitToSeeScreen(MainScreen())
        Then.IShouldSeeText("Hello world")
    }
}

Jetpack Compose activity:

class TestBasicComposeActivity : SampleBaseIntegrationTestCase() {
    @get:Rule
    val composeTestRule = createEmptyComposeRule()

    @Test
    fun testComposeScreenDisplays() {
        Given.IRenderScreen(ComposeScreen(), composeTestRule)
        When.IWaitToSeeScreen(ComposeScreen(), composeTestRule)
        Then.IShouldSeeText("Hello user!", composeTestRule)
    }
}

Hybrid screens work too. Just use the espresso steps to assert or perform an action on XML views, and compose steps for compose nodes.

Kherkin Functionality

There are 3 major components of the Kherkin framework: Base Test Case, Step Definitions, and Screen Objects.

BaseIntegrationTestCase

The BaseIntegrationTestCase is the foundation of every test class created from this framework. It provides several features that can be utilized by the tests within each test class.

  1. Timeout: Time before a test fails (default 60)
  2. TestWatcher: Logs success or failure, records screenshot for failures
  3. Setup: Required setup before each test, can be overridden
  4. TearDown: Required tear down after each test, resets preconditions, can be overridden
  5. Permissions: Not included in the base, but this is where permissions need to be granted before tests can run based on your app's permission requirements.

Step Definitions

Kherkin step definitions are divided into three types:

  1. Setup
  2. Action
  3. Assertion

Note: Espresso, the library for testing XML based layouts, is not compatible with screens made with Jetpack Compose. Some steps are duplicated, existing in both with the same wording but taking different parameters. Resource identifiers are unique to XML layouts, while test tags are a string unique to Compose code. Compose steps also must pass in the ComposeRule every single time. Espresso examples in code blocks will come first, followed by the Compose equivalent step which has the ComposeRule variable.

Setup

Setup step definitions are used for setting up and rendering the screens being tested.

// Launch activity
Given.IRenderScreen(MainScreen())
Given.IRenderScreen(ComposeScreen(), composeTestRule)

// Navigate from starting activity to another via a set of step definitions
Given.INavigateToScreen(FinalScreen())
Given.INavigateToScreen(SecondComposeScreen(), composeTestRule)

Action

Action step definitions are used to interact with views using their text content, resource IDs, tags, and other attributes.

// Touch button with matching resource ID and text
When.ITouchButtonWithText(R.id.button, "Button text")
// Touch node with click action and matching text
When.ITouchText("Click Me", composeTestRule)

// Touch view with matching tag
When.ITouchViewWithTag("tag")
When.ITouchNode("test tag", composeTestRule)

// Type a string into a text field with matching resource ID
When.IEnterTextIntoField(R.id.textfield, "text to enter")
// Type a string into a node with a set text action and a matching tag
When.IEnterTextIntoField("test tag", "text to enter", composeTestRule)

Assertion

Assertion step definitions are used to verify view attributes by their content (text, resource IDs), status (enabled, visible), or type (EditText, Button).

// Assert view of type EditText has matching resource ID and hint
Then.IShouldSeeTextFieldWithHint(R.id.textfield, "Hint text")
// Assert node with click action and matching tag is displayed
Then.IShouldSeeNodeIsClickable("Compose Button", composeTestRule)

// Assert view of type RadioButton with matching text is disabled
Then.IShouldSeeRadioButtonWithTextIsDisabled("Disabled button")

// Assert view with matching resource ID is not displayed on the screen
Then.IShouldNotSeeView(R.id.view)
// Assert node with matching tag is not displayed on the screen
Then.IShouldNotSeeNode("Hidden Button", composeTestRule)

Screen Objects

Each activity you want to test needs a corresponding class that inherits from Screen(). Optionally, if that screen will be part of an end to end test that utilizes the INavigateToScreen function, it must also inherit from Navigable.

Each screen file must have:

  1. A way to start and drive your activity's lifecycle
  2. A unique trait that is used to identify your activity
  3. The name of your activity
  4. A function to render the activity
  5. A list of entry points to aid in navigation to the activity during flow testing

Contributing

Please see the contributing file on how to contribute to this framework.

Code of Conduct

Help us make this project open and inclusive. Please follow our Code of Conduct.

About

Kherkin is an Android UI testing framework that simplifies writing UI tests

Resources

License

Code of conduct

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages