Hot questions for Using Cucumber in android

Question:

I have been trying to get some cucumber tests written for an Android app running from command line. Since it is a maven project I have been using the following command:

mvn test 

Maven then proceeds to run tests.BaseTest which builds successfully. Except no tests run because tests.BaseTest is not the test runner. The test runner is CucumberTest. To change this I then use the following command:

mvn -Dtest=CucumberTest -e 

Maven now runs the test runner and decides to ruin my day by failing to build and throwing this mess at me (-e flag for stack trace, if yall need debug let me know):

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.12.4:test (default-test) on project halo.home.qa: No tests were executed!  (Set -DfailIfNoTests=false to ignore this error.) -> [Help 1]
org.apache.maven.lifecycle.LifecycleExecutionException: Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.12.4:test (default-test) on project halo.home.qa: No tests were executed!  (Set -DfailIfNoTests=false to ignore this error.)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:215)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:156)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:148)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:117)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:81)
    at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build (SingleThreadedBuilder.java:56)
    at org.apache.maven.lifecycle.internal.LifecycleStarter.execute (LifecycleStarter.java:128)
    at org.apache.maven.DefaultMaven.doExecute (DefaultMaven.java:305)
    at org.apache.maven.DefaultMaven.doExecute (DefaultMaven.java:192)
    at org.apache.maven.DefaultMaven.execute (DefaultMaven.java:105)
    at org.apache.maven.cli.MavenCli.execute (MavenCli.java:956)
    at org.apache.maven.cli.MavenCli.doMain (MavenCli.java:288)
    at org.apache.maven.cli.MavenCli.main (MavenCli.java:192)
    at sun.reflect.NativeMethodAccessorImpl.invoke0 (Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke (Method.java:498)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced (Launcher.java:289)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launch (Launcher.java:229)
    at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode (Launcher.java:415)
    at org.codehaus.plexus.classworlds.launcher.Launcher.main (Launcher.java:356)
Caused by: org.apache.maven.plugin.MojoFailureException: No tests were executed!  (Set -DfailIfNoTests=false to ignore this error.)
    at org.apache.maven.plugin.surefire.AbstractSurefireMojo.execute (AbstractSurefireMojo.java:579)
    at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo (DefaultBuildPluginManager.java:137)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:210)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:156)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:148)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:117)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:81)
    at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build (SingleThreadedBuilder.java:56)
    at org.apache.maven.lifecycle.internal.LifecycleStarter.execute (LifecycleStarter.java:128)
    at org.apache.maven.DefaultMaven.doExecute (DefaultMaven.java:305)
    at org.apache.maven.DefaultMaven.doExecute (DefaultMaven.java:192)
    at org.apache.maven.DefaultMaven.execute (DefaultMaven.java:105)
    at org.apache.maven.cli.MavenCli.execute (MavenCli.java:956)
    at org.apache.maven.cli.MavenCli.doMain (MavenCli.java:288)
    at org.apache.maven.cli.MavenCli.main (MavenCli.java:192)
    at sun.reflect.NativeMethodAccessorImpl.invoke0 (Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke (Method.java:498)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced (Launcher.java:289)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launch (Launcher.java:229)
    at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode (Launcher.java:415)
    at org.codehaus.plexus.classworlds.launcher.Launcher.main (Launcher.java:356)

Things I have tried: Ensuring the test class ended in the right suffix (*Test.java), adding the maven-surefire-plugin as a dependency in my pom file, changing version numbers of my dependencies in the pom file, going from java 11 to java 8.

This cucumber project works fine when I run this on IntelliJ, but I need to run through command line as well. Here is the project structure, and the CucumberTest runner:

Project
|
+--src
    |
    +--test
        |
        +--java
            |
            +--tests
            |    |
            |    +--BaseTest.java
            | 
            +-- CucumberTest.java

And the Runner:

import cucumber.api.CucumberOptions;
import cucumber.api.junit.Cucumber;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.jupiter.api.BeforeAll;
import org.junit.runner.RunWith;
import org.testng.annotations.BeforeSuite;
import tests.BaseTest;

import java.net.MalformedURLException;

@RunWith(Cucumber.class)
@CucumberOptions(
        plugin = {"html:target/Destination", "json:target/Destination/lpg/cucumber-json-report.json"},
        format = { "pretty", "html:target/Destination/lpg/cucumber-pretty"},
        features = {"src/test/resources"})

public class CucumberTest {
    @BeforeClass
    public static void setUp() throws MalformedURLException {
        BaseTest.runAppium();
    }

    @AfterClass
    public static void destroy () {
        BaseTest.destroy();
    }
}

Thanks for your help


Answer:

I think that problem occurs because you haven’t any Test in your cucumber class, try to create a test method (@Test) with anything.

Question:

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:

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

What am I missing here?


Answer:

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.

Question:

I am getting 2 issues 1- Cucumber Test functions in steps are not getting invoked unless i add @After or @Before before step class functions 2- The sequence of functions in steps are not accordingly. Last function getting called first and like wise. no regularity.

I am checking these by taking prints. Also by debugging.

This is what I have done:

In app/build.gradle

defaultConfig {
testApplicationId "com.my.app.test"
testInstrumentationRunner "com.my.app.test.CucumberInstrumentation"
}

sourceSets {
androidTest { assets.srcDirs = ['src/androidTest/assets'] }
}

buildTypes {
debug {
    testCoverageEnabled true
    buildConfigField "String", "TEST_TAGS", "\"${getTestTags()}\""
}
}

def getTestTags() {
    return project.hasProperty("tags") ? project.getProperties().get("tags") : ""
}

dependencies {
    androidTestImplementation 'info.cukes:cucumber-android:1.2.5'
    androidTestImplementation 'info.cukes:cucumber-picocontainer:1.2.5'
}

This is my feature file under the src/androidTest/assets/features directory.

Feature: Login
Perform login on email and password are inputted

@login-scenarios
Scenario: Input email and password in correct format
Given   Login screen is launched
When    I input an email, "user@email.com"
When    I input a password, "abc12345"
When    I press on Log in button
Then    I Should get logged in and redirect to home screen

This is my Login StepDefinitions file under the src/androidTest/java/com/my/app/test directory.

public class LoginStepdefs {
    @Rule
    private ActivityTestRule<LoginActivity> activityTestRule = new ActivityTestRule<>(LoginActivity.class);

    private LoginActivity activity;

    final static String Tag = "CucumberLogin: ";

    @Before("@login-scenarios")
    public void setUp() {
    System.out.println(Tag + "setUp in LoginStepdefs");
    activityTestRule.launchActivity(new Intent());
    activity = activityTestRule.getActivity();
    }

    @After("@login-scenarios")
    @Given("^Login screen is launched$")
    public void testLoginScreenIsLaunched() {
    System.out.println(Tag + "testLoginScreenIsLaunched");
    }

    @When("^I input an email, \"([^\"]*)\"$")
    public void iInputAnEmail(String email) {
    System.out.println(Tag + "iInputAnEmail: " + email);
    }

    @When("^I input a password, \"([^\"]*)\"$")
    public void iInputAPassword(String password) {
    System.out.println(Tag + "iInputAPassword: " + password);
    }

    @When("^I press on Log in button$")
    public void iPressOnLogInButton() {
    System.out.println(Tag + "iPressOnLogInButton");
    }

    @Then("^I Should get logged in and redirect to home screen$")
    public void iShouldGetLoggedInAndRedirectToHomeScreen() {
    System.out.println(Tag + "iShouldGetLoggedInAndRedirectToHomeScreen");
    }

    public void tearDown() {
    System.out.println(Tag + "tearDown in LoginStepdefs");
    activityTestRule.finishActivity();
    }
}

Then this is my Runner file.

@CucumberOptions(
    features = "features",
    glue = "com.my.app.test")
public class CucumberInstrumentation extends MonitoringInstrumentation {

    private final CucumberInstrumentationCore instrumentationCore = new CucumberInstrumentationCore(this);

    @Override
    public void onCreate(Bundle arguments) {
    super.onCreate(arguments);

    instrumentationCore.create(arguments);
    start();
    }
    @Override
    public void onStart() {
    super.onStart();

    waitForIdleSync();
    instrumentationCore.start();
    }
}

Now I am trying to run the cucumber tests with Android Studio like this:

  • Open "Edit Configuration"
  • Click + on left panel and select "Android Instrumented Tests"
  • Put a name you like to remember with at the Name field on top and select OK.
  • Click "Run"

Now i am getting prints in this sequence. Which is wrong.

CucumberLogin: setUp in LoginStepdefs
CucumberLogin: iPressOnLogInButton
CucumberLogin: iShouldGetLoggedInAndRedirectToHomeScreen
CucumberLogin: tearDown in LoginStepdefs
CucumberLogin: testLoginScreenIsLaunched

These are complete logs

E/libprocessgroup: failed to make and chown /acct/uid_10088: Read-only file system
04-12 12:04:07.246 3481-3481/? W/Zygote: createProcessGroup failed, kernel missing CONFIG_CGROUP_CPUACCT?
04-12 12:04:07.246 3481-3481/? I/art: Not late-enabling -Xcheck:jni (already on)
04-12 12:04:07.262 3481-3481/com.my.app W/System.err: java.io.FileNotFoundException: /jacoco.exec: open failed: EROFS (Read-only file system)
04-12 12:04:07.263 3481-3481/com.my.app W/System.err:     at libcore.io.IoBridge.open(IoBridge.java:456)
04-12 12:04:07.263 3481-3481/com.my.app W/System.err:     at java.io.FileOutputStream.<init>(FileOutputStream.java:87)
04-12 12:04:07.263 3481-3481/com.my.app W/System.err:     at org.jacoco.agent.rt.internal_8ff85ea.output.FileOutput.openFile(FileOutput.java:67)
04-12 12:04:07.263 3481-3481/com.my.app W/System.err:     at org.jacoco.agent.rt.internal_8ff85ea.output.FileOutput.startup(FileOutput.java:49)
04-12 12:04:07.263 3481-3481/com.my.app W/System.err:     at org.jacoco.agent.rt.internal_8ff85ea.Agent.startup(Agent.java:122)
04-12 12:04:07.263 3481-3481/com.my.app W/System.err:     at org.jacoco.agent.rt.internal_8ff85ea.Agent.getInstance(Agent.java:50)
04-12 12:04:07.263 3481-3481/com.my.app W/System.err:     at org.jacoco.agent.rt.internal_8ff85ea.Offline.<clinit>(Offline.java:31)
04-12 12:04:07.263 3481-3481/com.my.app W/System.err:     at com.my.app.di.MyApp.$jacocoInit(MyApp.java)
04-12 12:04:07.263 3481-3481/com.my.app W/System.err:     at com.my.app.di.MyApp.<init>(MyApp.java)
04-12 12:04:07.263 3481-3481/com.my.app W/System.err:     at java.lang.reflect.Constructor.newInstance(Native Method)
04-12 12:04:07.263 3481-3481/com.my.app W/System.err:     at java.lang.Class.newInstance(Class.java:1606)
04-12 12:04:07.263 3481-3481/com.my.app W/System.err:     at android.app.Instrumentation.newApplication(Instrumentation.java:995)
04-12 12:04:07.263 3481-3481/com.my.app W/System.err:     at android.app.Instrumentation.newApplication(Instrumentation.java:980)
04-12 12:04:07.263 3481-3481/com.my.app W/System.err:     at android.app.LoadedApk.makeApplication(LoadedApk.java:558)
04-12 12:04:07.263 3481-3481/com.my.app W/System.err:     at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4526)
04-12 12:04:07.263 3481-3481/com.my.app W/System.err:     at android.app.ActivityThread.access$1500(ActivityThread.java:151)
04-12 12:04:07.263 3481-3481/com.my.app W/System.err:     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1364)
04-12 12:04:07.263 3481-3481/com.my.app W/System.err:     at android.os.Handler.dispatchMessage(Handler.java:102)
04-12 12:04:07.263 3481-3481/com.my.app W/System.err:     at android.os.Looper.loop(Looper.java:135)
04-12 12:04:07.263 3481-3481/com.my.app W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:5254)
04-12 12:04:07.263 3481-3481/com.my.app W/System.err:     at java.lang.reflect.Method.invoke(Native Method)
04-12 12:04:07.263 3481-3481/com.my.app W/System.err:     at java.lang.reflect.Method.invoke(Method.java:372)
04-12 12:04:07.263 3481-3481/com.my.app W/System.err:     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
04-12 12:04:07.263 3481-3481/com.my.app W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
04-12 12:04:07.263 3481-3481/com.my.app W/System.err: Caused by: android.system.ErrnoException: open failed: EROFS (Read-only file system)
04-12 12:04:07.263 3481-3481/com.my.app W/System.err:     at libcore.io.Posix.open(Native Method)
04-12 12:04:07.263 3481-3481/com.my.app W/System.err:     at libcore.io.BlockGuardOs.open(BlockGuardOs.java:186)
04-12 12:04:07.263 3481-3481/com.my.app W/System.err:     at libcore.io.IoBridge.open(IoBridge.java:442)
04-12 12:04:07.263 3481-3481/com.my.app W/System.err:   ... 23 more
04-12 12:04:07.264 3481-3481/com.my.app I/MultiDex: VM with version 2.1.0 has multidex support
04-12 12:04:07.264 3481-3481/com.my.app I/MultiDex: Installing application
04-12 12:04:07.264 3481-3481/com.my.app I/MultiDex: VM has multidex support, MultiDex support library is disabled.
04-12 12:04:07.291 3481-3481/com.my.app I/MonitoringInstr: Instrumentation started on process com.my.app
04-12 12:04:07.298 3481-3481/com.my.app I/MonitoringInstr: Setting context classloader to 'dalvik.system.PathClassLoader[DexPathList[[zip file "/system/framework/android.test.runner.jar", zip file "/data/app/com.my.app.test-2/base.apk", zip file "/data/app/com.my.app-2/base.apk"],nativeLibraryDirectories=[/data/app/com.my.app-2/lib/x86, /vendor/lib, /system/lib]]]', Original: 'dalvik.system.PathClassLoader[DexPathList[[zip file "/system/framework/android.test.runner.jar", zip file "/data/app/com.my.app.test-2/base.apk", zip file "/data/app/com.my.app-2/base.apk"],nativeLibraryDirectories=[/data/app/com.my.app-2/lib/x86, /vendor/lib, /system/lib]]]'
04-12 12:04:07.321 3481-3481/com.my.app D/cucumber-android: Found CucumberOptions in class com.my.app.test.CucumberInstrumentation
04-12 12:04:07.454 3481-3512/com.my.app I/MonitoringInstr: Setting context classloader to 'dalvik.system.PathClassLoader[DexPathList[[zip file "/system/framework/android.test.runner.jar", zip file "/data/app/com.my.app.test-2/base.apk", zip file "/data/app/com.my.app-2/base.apk"],nativeLibraryDirectories=[/data/app/com.my.app-2/lib/x86, /vendor/lib, /system/lib]]]', Original: 'dalvik.system.PathClassLoader[DexPathList[[zip file "/system/framework/android.test.runner.jar", zip file "/data/app/com.my.app.test-2/base.apk", zip file "/data/app/com.my.app-2/base.apk"],nativeLibraryDirectories=[/data/app/com.my.app-2/lib/x86, /vendor/lib, /system/lib]]]'
04-12 12:04:07.456 3481-3508/com.my.app W/AnalyticsUserIDStore: initStore should have been called before calling setUserID
04-12 12:04:07.456 3481-3512/com.my.app D/cucumber-android: Feature: Login (features/login.feature)
    Perform login on email and password are inputted
04-12 12:04:07.457 3481-3508/com.my.app W/UserDataStore: initStore should have been called before calling setUserID
04-12 12:04:07.462 3481-3512/com.my.app I/System.out: CucumberLogin: setUp in LoginStepdefs
04-12 12:04:07.468 3481-3481/com.my.app D/LifecycleMonitor: Lifecycle status change: com.my.app.activities.LoginActivity@1958eb70 in: PRE_ON_CREATE
04-12 12:04:07.470 3481-3481/com.my.app W/art: Before Android 4.1, method android.graphics.PorterDuffColorFilter android.support.graphics.drawable.VectorDrawableCompat.updateTintFilter(android.graphics.PorterDuffColorFilter, android.content.res.ColorStateList, android.graphics.PorterDuff$Mode) would have incorrectly overridden the package-private method in android.graphics.drawable.Drawable
04-12 12:04:07.493 3481-3481/com.my.app I/TextInputLayout: EditText added is not a TextInputEditText. Please switch to using that class instead.
04-12 12:04:07.495 3481-3481/com.my.app I/TextInputLayout: EditText added is not a TextInputEditText. Please switch to using that class instead.
04-12 12:04:07.497 3481-3481/com.my.app D/LifecycleMonitor: Lifecycle status change: com.my.app.activities.LoginActivity@1958eb70 in: CREATED
04-12 12:04:07.497 3481-3481/com.my.app D/LifecycleMonitor: Lifecycle status change: com.my.app.activities.LoginActivity@1958eb70 in: STARTED
04-12 12:04:07.497 3481-3481/com.my.app D/LifecycleMonitor: Lifecycle status change: com.my.app.activities.LoginActivity@1958eb70 in: RESUMED
04-12 12:04:07.499 3481-3519/com.my.app D/OpenGLRenderer: Use EGL_SWAP_BEHAVIOR_PRESERVED: true
04-12 12:04:07.500 3481-3481/com.my.app D/Atlas: Validating map...
04-12 12:04:07.527 3481-3519/com.my.app I/OpenGLRenderer: Initialized EGL, version 1.4
04-12 12:04:07.527 3481-3519/com.my.app W/OpenGLRenderer: Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without...
04-12 12:04:07.532 3481-3519/com.my.app D/EGL_emulation: eglCreateContext: 0xa1805dc0: maj 2 min 0 rcv 2
04-12 12:04:07.536 3481-3519/com.my.app D/EGL_emulation: eglMakeCurrent: 0xa1805dc0: ver 2 0
04-12 12:04:07.537 3481-3519/com.my.app D/OpenGLRenderer: Enabling debug mode 0
04-12 12:04:07.541 3481-3519/com.my.app D/EGL_emulation: eglMakeCurrent: 0xa1805dc0: ver 2 0
04-12 12:04:07.601 3481-3512/com.my.app D/cucumber-android: Scenario: Input email and password in correct format
04-12 12:04:07.602 3481-3512/com.my.app I/System.out: CucumberLogin: iPressOnLogInButton
04-12 12:04:07.603 3481-3512/com.my.app I/System.out: CucumberLogin: iShouldGetLoggedInAndRedirectToHomeScreen
04-12 12:04:07.603 3481-3512/com.my.app I/System.out: CucumberLogin: tearDown in LoginStepdefs
04-12 12:04:07.631 3481-3481/com.my.app D/LifecycleMonitor: Lifecycle status change: com.my.app.activities.LoginActivity@1958eb70 in: PAUSED
04-12 12:04:07.631 3481-3481/com.my.app D/LifecycleMonitor: running callback: android.support.test.rule.ActivityTestRule$LifecycleCallback@19928631
04-12 12:04:07.631 3481-3481/com.my.app D/LifecycleMonitor: callback completes: android.support.test.rule.ActivityTestRule$LifecycleCallback@19928631
04-12 12:04:07.639 3481-3512/com.my.app I/System.out: CucumberLogin: testLoginScreenIsLaunched
04-12 12:04:07.640 3481-3512/com.my.app I/MonitoringInstr: Unstopped activity count: 1
04-12 12:04:07.641 3481-3481/com.my.app I/MonitoringInstr: Activities that are still in CREATED to STOPPED: 1
04-12 12:04:07.665 3481-3519/com.my.app D/EGL_emulation: eglMakeCurrent: 0xa1805dc0: ver 2 0
04-12 12:04:07.690 3481-3512/com.my.app I/MonitoringInstr: Unstopped activity count: 1
04-12 12:04:07.719 3481-3481/com.my.app W/IInputConnectionWrapper: showStatusIcon on inactive InputConnection
04-12 12:04:07.741 3481-3512/com.my.app I/MonitoringInstr: Unstopped activity count: 1
04-12 12:04:07.763 3481-3481/com.my.app D/LifecycleMonitor: Lifecycle status change: com.my.app.activities.LoginActivity@1958eb70 in: STOPPED
04-12 12:04:07.763 3481-3481/com.my.app D/LifecycleMonitor: running callback: android.support.test.rule.ActivityTestRule$LifecycleCallback@19928631
04-12 12:04:07.763 3481-3481/com.my.app D/LifecycleMonitor: callback completes: android.support.test.rule.ActivityTestRule$LifecycleCallback@19928631
04-12 12:04:07.763 3481-3481/com.my.app D/LifecycleMonitor: Lifecycle status change: com.my.app.activities.LoginActivity@1958eb70 in: DESTROYED
04-12 12:04:07.763 3481-3481/com.my.app D/LifecycleMonitor: running callback: android.support.test.rule.ActivityTestRule$LifecycleCallback@19928631
04-12 12:04:07.763 3481-3481/com.my.app D/LifecycleMonitor: callback completes: android.support.test.rule.ActivityTestRule$LifecycleCallback@19928631
04-12 12:04:07.791 3481-3512/com.my.app I/MonitoringInstr: waitForActivitiesToComplete() took: 151ms

Please help me

My setup: Gradle Version: 5.0 Android Plugin Version: 3.3.0 Cucumber Library Version: 1.2.5


Answer:

I got this problem resolved. and my tests are running fine now.

I did following changes from the above mentioned code.

Added this line in dependencies along with cucumber dependencies

androidTestImplementation 'com.android.support:support-annotations:27.1.1'

Also added this line in @CucumberOptions in my Custom cucumber runner

format = {"pretty"},

Which makes @CucumberOptions as:

@CucumberOptions(
features = "features",
format = {"pretty"},
glue = "com.my.app.test.steps")

Other then that my code is same as mentioned above. And working now.

Question:

Trying to reuse a method in another class, initialising the class like this:

public class SettingsStepDefs {
public Scenario scenario;

@Autowired
public AndroidBase androidBase;
public GenericStepDefs genericStepDefs;


@Before
public void before(Scenario scenario) {
    this.scenario = scenario;
    genericStepDefs = new GenericStepDefs();

and then later using the method

genericStepDefs.iTapDone();

However i am getting a Null pointer exception on the line above.

The code in iTapDone() is functional, if I copy the line and use it on SettingsStepDefs, it works.

Thank you in advance.


Answer:

You need to share an instance of GenericStepDefs between the steps. Since the variable genericStepDefs isn't set, you will get a NullPointerException.

You seem to be using Spring since you use the annotation @Autowired. Look into sharing state with Spring for Cucumber. It is done by setting up Spring for Cucumber-JVM. The dependency

<dependency>
    <groupId>info.cukes</groupId>
    <artifactId>cucumber-spring</artifactId>
    <version>1.2.5</version>
</dependency>

is needed.

Question:

I have an Android project (on Windows) where I am trying to run cucumber-jvm as a non-instrumented "unit test". I.e. execute Cucumber features when I run gradlew test.

Here are the relevant bits of my app's build.gradle:

android {
    ...
    testOptions {
        unitTests.all {
            javaexec {
                main = "cucumber.api.cli.Main"
                classpath = getClasspath()
                args = ['--plugin', 'pretty', '--glue', 'gradle.cucumber', 'src/test/java/cucumber/assets']
            }
        }
    }
}
dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation 'com.android.support:appcompat-v7:28.0.0-rc02'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    testImplementation 'junit:junit:4.12'
    testImplementation 'io.cucumber:cucumber-java:3.0.2'
    testImplementation 'io.cucumber:cucumber-junit:3.0.2'
}

When I run gradlew test --info at the command-line I get the following error:

Starting process 'command 'C:\Program Files\Java\jdk1.8.0_162\bin\java.exe''. Working directory: C:\dev\urig\android-cucumber\app Command: C:\Program Files\Java\jdk1.8.0_162\bin\java.exe -Dfile.encoding=windows-1252 -Duser.country=US -Duser.language=en -Duser.variant cucumber.api.cli.Main --plugin pretty --glue gradle.cucumber src/test/java/cucumber/assets
Successfully started process 'command 'C:\Program Files\Java\jdk1.8.0_162\bin\java.exe''
Error: Could not find or load main class cucumber.api.cli.Main

It looks to me like the command contains no classpath and my question is - Why?

PS - I've verified that at the time of the call to javaexec the call to getClasspath() indeed contains all the dependencies with this little bit of Groovy: println getClasspath().any { println it }

PPS - I know the intended use of cucumber-jvm is for instrumented tests using cucumber-android. I have a specific use case for running Cucumber as a "local unit test" (Android terms, not mine) so the above doesn't quite help me.


Answer:

I believe I've found a solution for my issue. Here's the code that works for me:

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']
        }
    }
}

It seems to me that my original call to getClassPath() inside the javaexec closure was returning an empty file collection. At the same time, in the closure for unitTests.all, getClassPath() contains the correct classpath.

By passing the classpath from the external closure into the internal closure via a variable, cucumber.api.cli.Main now runs successfully and my Cucumber features run as part of the Gradle test task.

Question:

I have configured cucumber tests. Working fine for my activity. I have also configured Appium UI Tests which are also working fine when invoked. Now i want to invoke my activity's UI Elements with Appium under Cucumber tests which are written in stepdefinitions file.

My build.gradle

// Cucumber for E2E testing
androidTestImplementation 'com.android.support:support-annotations:27.1.1'
androidTestImplementation 'info.cukes:cucumber-android:1.2.5'
androidTestImplementation 'info.cukes:cucumber-picocontainer:1.2.5'

// Appium for DI  -- implementation / compileOnly
implementation 'io.appium:java-client:6.1.0'
implementation files('libs/java-client-6.1.0.jar')
implementation files('libs/selenium-server-standalone-3.13.0.jar')

In Cucumber steps folder. I have written tests which are working fine. Checked through logs. Now when i add code of appium in steps setup function to invoke LoginActivity and i can access UI elements like this:

@Before("@login-scenarios")
public void setUp() throws java.net.MalformedURLException {
System.out.println(Tag + "start - setUp in LoginStepdefs");

org.openqa.selenium.remote.DesiredCapabilities capabilities = new org.openqa.selenium.remote.DesiredCapabilities();
capabilities.setCapability("deviceName", "Emulator Nexus_4_API_22 Android 5.1.1, API 22");
capabilities.setCapability(org.openqa.selenium.remote.CapabilityType.PLATFORM, "Android");
capabilities.setCapability(org.openqa.selenium.remote.CapabilityType.VERSION, "5.1.1");
capabilities.setCapability("platformName", "Android");
capabilities.setCapability("appPackage", "com.my.app");
capabilities.setCapability("appActivity", ".activities.MainActivity");
capabilities.setCapability("unicodeKeyboard", true);
capabilities.setCapability("resetKeyboard", true);
driver = new io.appium.java_client.AppiumDriver(new java.net.URL("http://127.0.0.1:1234/wd/hub"), capabilities);
driver.manage().timeouts().implicitlyWait(5, java.util.concurrent.TimeUnit.SECONDS);
}

@After("@login-scenarios")
public void tearDown() {
System.out.println(Tag + "stop - tearDown in LoginStepdefs");
driver.quit();
}

@Given("^Login screen is launched$")
public void loginScreenIsLaunched() {
System.out.println(Tag + "1 - loginScreenIsLaunched");
org.openqa.selenium.support.ui.WebDriverWait wait = new org.openqa.selenium.support.ui.WebDriverWait(driver, 10);
wait.until(org.openqa.selenium.support.ui.ExpectedConditions.presenceOfElementLocated(org.openqa.selenium.By.id(com.my.app.utilities.Constant.APP_PACKAGE + signinEmailId)));
}

Now when i run my cucumber tests. Alot of logs in console and after some time build failed.

I am posting logs. Where i am wrong??

    AGPBI: {"kind":"warning","text":"Type `javax.imageio.ImageIO` was not found, it is required for default or static interface methods desugaring of `void io.appium.java_client.ios.HasIOSClipboard.setClipboardImage(java.awt.image.BufferedImage)`","sources":[{"file":"/home/sajid/Git/project-android/app/libs/java-client-6.1.0.jar"}],"tool":"D8"}
    AGPBI: {"kind":"warning","text":"Type `javax.xml.bind.DatatypeConverter` was not found, it is required for default or static interface methods desugaring of `byte[] io.appium.java_client.InteractsWithFiles.pullFile(java.lang.String)`","sources":[{"file":"/home/sajid/Git/project-android/app/libs/java-client-6.1.0.jar"}],"tool":"D8"}
    AGPBI: {"kind":"warning","text":"Type `javax.imageio.ImageIO` was not found, it is required for default or static interface methods desugaring of `void io.appium.java_client.ios.HasIOSClipboard.setClipboardImage(java.awt.image.BufferedImage)`","sources":[{"file":"/home/sajid/.gradle/caches/modules-2/files-2.1/io.appium/java-client/6.1.0/6bb22d04886b87f525e4ea352b1940a6ff8c471b/java-client-6.1.0.jar"}],"tool":"D8"}
    AGPBI: {"kind":"warning","text":"Type `javax.xml.bind.DatatypeConverter` was not found, it is required for default or static interface methods desugaring of `byte[] io.appium.java_client.InteractsWithFiles.pullFile(java.lang.String)`","sources":[{"file":"/home/sajid/.gradle/caches/modules-2/files-2.1/io.appium/java-client/6.1.0/6bb22d04886b87f525e4ea352b1940a6ff8c471b/java-client-6.1.0.jar"}],"tool":"D8"}
    JarCode: JSR encountered; reparse using JSRInlinerAdapter
    AGPBI: {"kind":"warning","text":"Type `javax.imageio.ImageIO` was not found, it is required for default or static interface methods desugaring of `void org.eclipse.jetty.util.preventers.AppContextLeakPreventer.prevent(java.lang.ClassLoader)`","sources":[{"file":"/home/sajid/Git/project-android/app/libs/selenium-server-standalone-3.13.0.jar"}],"tool":"D8"}
    AGPBI: {"kind":"warning","text":"Type `org.apache.log.Hierarchy` was not found, it is required for default or static interface methods desugaring of `org.apache.log.Logger org.apache.commons.logging.impl.LogKitLogger.getLogger()`","sources":[{"file":"/home/sajid/Git/project-android/app/libs/selenium-server-standalone-3.13.0.jar"}],"tool":"D8"}
    JarCode: JSR encountered; reparse using JSRInlinerAdapter
    JarCode: JSR encountered; reparse using JSRInlinerAdapter
    JarCode: JSR encountered; reparse using JSRInlinerAdapter
    Expiring Daemon because JVM Tenured space is exhausted
    Daemon will be stopped at the end of the build after running out of JVM memory
    AGPBI: {"kind":"warning","text":"Type `sun.misc.Unsafe` was not found, it is required for default or static interface methods desugaring of `void com.google.common.util.concurrent.AbstractFuture$UnsafeAtomicHelper.\u003cclinit\u003e()`","sources":[{"file":"/home/sajid/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/27.0.1-android/b7e1c37f66ef193796ccd7ea6e80c2b05426182d/guava-27.0.1-android.jar"}],"tool":"D8"}
    AGPBI: {"kind":"warning","text":"Type `sun.misc.Unsafe` was not found, it is required for default or static interface methods desugaring of `sun.misc.Unsafe com.google.common.cache.Striped64.getUnsafe()`","sources":[{"file":"/home/sajid/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/27.0.1-android/b7e1c37f66ef193796ccd7ea6e80c2b05426182d/guava-27.0.1-android.jar"}],"tool":"D8"}
    AGPBI: {"kind":"warning","text":"Type `javax.imageio.ImageIO` was not found, it is required for default or static interface methods desugaring of `void org.seleniumhq.jetty9.util.preventers.AppContextLeakPreventer.prevent(java.lang.ClassLoader)`","sources":[{"file":"/home/sajid/Git/project-android/app/libs/selenium-server-standalone-3.13.0.jar"}],"tool":"D8"}
    JarCode: JSR encountered; reparse using JSRInlinerAdapter
    AGPBI: {"kind":"warning","text":"Type `sun.misc.Unsafe` was not found, it is required for default or static interface methods desugaring of `sun.misc.Unsafe com.google.common.hash.LittleEndianByteArray$UnsafeByteArray.getUnsafe()`","sources":[{"file":"/home/sajid/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/27.0.1-android/b7e1c37f66ef193796ccd7ea6e80c2b05426182d/guava-27.0.1-android.jar"}],"tool":"D8"}
    > Task :app:transformClassesWithDexBuilderForDebug

    Expiring Daemon because JVM Tenured space is exhausted
    AGPBI: {"kind":"warning","text":"Type `javax.swing.JTree` was not found, it is required for default or static interface methods desugaring of `void net.sourceforge.htmlunit.corejs.javascript.tools.debugger.treetable.JTreeTable$TreeTableCellRenderer.updateUI()`","sources":[{"file":"/home/sajid/Git/project-android/app/libs/selenium-server-standalone-3.13.0.jar"}],"tool":"D8"}
    AGPBI: {"kind":"warning","text":"Type `javax.swing.UIManager` was not found, it is required for default or static interface methods desugaring of `void net.sourceforge.htmlunit.corejs.javascript.tools.debugger.treetable.JTreeTable$TreeTableCellRenderer.updateUI()`","sources":[{"file":"/home/sajid/Git/project-android/app/libs/selenium-server-standalone-3.13.0.jar"}],"tool":"D8"}
    JarCode: JSR encountered; reparse using JSRInlinerAdapter
    AGPBI: {"kind":"warning","text":"Type `sun.misc.Unsafe` was not found, it is required for default or static interface methods desugaring of `void com.google.common.util.concurrent.AbstractFuture$UnsafeAtomicHelper.\u003cclinit\u003e()`","sources":[{"file":"/home/sajid/Git/project-android/app/libs/selenium-server-standalone-3.13.0.jar"}],"tool":"D8"}




> Task :app:transformClassesWithDexBuilderForDebug FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':app:transformClassesWithDexBuilderForDebug'.
> com.android.build.api.transform.TransformException: com.android.builder.dexing.DexArchiveBuilderException: com.android.builder.dexing.DexArchiveBuilderException: Failed to process /home/sajid/Git/project-android/app/libs/selenium-server-standalone-3.13.0.jar

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

Deprecated Gradle features were used in this build, making it incompatible with Gradle 6.0.
Use '--warning-mode all' to show the individual deprecation warnings.
See https://docs.gradle.org/5.0/userguide/command_line_interface.html#sec:command_line_warnings

BUILD FAILED in 5m 58s
21 actionable tasks: 6 executed, 15 up-to-date
Expiring Daemon because JVM Tenured space is exhausted

Answer:

It appears that you're trying to build an Appium-based test with Android SDK and compilation fails as Appium relies on Java SDK

So the architecture of your test should look like:

  1. Application compiled with the Android SDK deployed on Android device (or emulator)
  2. Your tests compiled with the Java SDK deployed on your computer
  3. Android device (or emulator) connected to the computer via ADB

This way your tests will control Android device and perform clicks, swipes, typing, assertions, etc. So you need to reconsider the approach of building your test project.

If you want UI tests to be an integral part of your Android application build consider switching to Espresso, check out How to Get Started with Espresso (Android) to learn more about the concept/implementation.