urig
urig

Reputation: 16831

How to make Cucumber features run in an Android project as local unit tests?

I have an Android project written in Java that I'm working on in Android Studio.

I'd like to use Cucumber for integration testing of some internal components (note: I know this is not the BDD way, nonetheless useful to me). I want the tests to run as local unit tests (without Instrumentation) using gradlew test because the components under test do not interact with the Android SDK.

My problem is that the Cucumber features are not recognized by Gradle and do not run when I run gradlew test.

Here's how I set it up so far:

  1. Added these dependencies to my app's build.gradle:

    testImplementation 'io.cucumber:cucumber-java:3.0.2'
    testImplementation 'io.cucumber:cucumber-junit:3.0.2'
    testImplementation 'io.cucumber:cucumber-jvm:3.0.2'
    
  2. Also there, I added the path to where I've put my Feature file:

    android {
        ...
        sourceSets {
            test {
                assets.srcDirs = ['src/test/java/integrationTest/assets']
            }
        }
    }
    

    This is based on this folder structure:

    enter image description here

  3. Added a class for the steps (Steps1.java) as can be seen above.

What am I missing here?

Upvotes: 2

Views: 3176

Answers (3)

Uddhav P. Gautam
Uddhav P. Gautam

Reputation: 7626

Or there is another way to directly run android unit tests from a custom runner. This is easiest approach for kotlin Android project.

In my app-level build.gradle,

dependencies {
   //Cucumber
   testImplementation("io.cucumber:cucumber-java:7.13.0")
   testImplementation("io.cucumber:cucumber-junit:7.13.0")
   testImplementation("io.cucumber:cucumber-jvm:7.13.0")
}

Then as shown in picture below, I managed to run a cucumber scenario through the CucumberTestRunner green run arrow.

enter image description here

Sample Source Code

For any entity data class UserEntity.kt,

import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "users")
data class UserEntity(
    @PrimaryKey val id: Int,
    val name: String
)

I first, created a feature file named UserEntityBdd.feature as,

Feature: UserEntity class behavior

  Scenario: Create a new UserEntity
    Given a user ID
    And a user name
    When a UserEntity is created
    Then the UserEntity should have the given ID and name

Then, corresponding steps definitions file named UserEntityStepDefinitions.kt as,

import com.example.mvvm.datalayer.model.UserEntity
import io.cucumber.java.en.Given
import io.cucumber.java.en.Then
import io.cucumber.java.en.When
import org.junit.Assert.assertEquals
import kotlin.properties.Delegates

class UserEntityStepDefinitions {
    private var userId by Delegates.notNull<Int>()
    private lateinit var userName: String
    private lateinit var userEntity: UserEntity

    @Given("a user ID")
    fun givenUserId() {
        userId = 123
    }

    @Given("a user name")
    fun givenUserName() {
        userName = "John Doe"
    }

    @When("a UserEntity is created")
    fun createUserEntity() {
        userEntity = UserEntity(userId, userName)
    }

    @Then("the UserEntity should have the given ID and name")
    fun checkUserEntity() {
        assertEquals(userId, userEntity.id)
        assertEquals(userName, userEntity.name)
    }
}

and finally the Cuccumber Custom Runner, named CucumberTestRunner.kt, as

import io.cucumber.junit.Cucumber
import io.cucumber.junit.CucumberOptions
import org.junit.runner.RunWith

@RunWith(Cucumber::class)
@CucumberOptions(
    features = ["."], // The package where your feature files are located
    glue = ["."], // The package where your step definitions are located
    plugin = ["pretty", "html:reports/test-report"] // Report plugins
)
class CucumberTestRunner

And doing this approach made me I don't need to convert/port below groovy snippet to kts

testOptions {
        unitTests.all {
            def classpath2 = getClasspath()
            javaexec {
                main = "cucumber.api.cli.Main"
                classpath = classpath2
                args = ['--plugin', 'pretty', '--glue', 'gradle.cucumber', 'src/test/java/cucumber/assets']
            }
        }
    }

Upvotes: 0

urig
urig

Reputation: 16831

I've found out how to configure my Android app to run my Cucumber tests as "local unit tests" when I run gradlew test from the command line. This is based on my SO question here and I've written a "HOWTO" blog post about it here: Android: Run Cucumber tests without a device or an emulator.

The key to getting it done is in the app's build.gradle file. By latching on to the unitTests.all hook in the testOptions section I am able to run Cucumber using javaexec like so:

android {
    ...
    testOptions {
        unitTests.all {
            def classpath2 = getClasspath()
            javaexec {
                main = "cucumber.api.cli.Main"
                classpath = classpath2
                args = ['--plugin', 'pretty', '--glue', 'gradle.cucumber', 'src/test/java/cucumber/assets']
            }
        }
    }
)

Upvotes: 1

M.P. Korstanje
M.P. Korstanje

Reputation: 12019

Your feature files are probably not getting picked up because you did not include a runner. You can either create a JUnit Runner or use the Gradle cucumber plugin. I am not sure if either would work in Android though.

Also you don't need io.cucumber:cucumber-jvm:3.0.2 as a dependency. It is only a pom.

Upvotes: 1

Related Questions