Wednesday, July 27, 2016

How to run Cucumber feature tests in android, quick steps for Android Studio 2.1





These steps are not for UI tests, if you want to do acceptance tests emulating user interaction with the UI better use Calabash.


  1. Create a new android project in Android Studio.
     
  2. Open build.gradle and add the following dependencies

    androidTestCompile 'junit:junit:4.12'
    androidTestCompile group: 'info.cukes', name: 'cucumber-java', version: '1.2.2'
    androidTestCompile group: 'info.cukes', name: 'cucumber-core', version: '1.2.2'
    androidTestCompile group: 'info.cukes', name: 'cucumber-html', version: '0.2.3'
    androidTestCompile group: 'info.cukes', name: 'cucumber-jvm-deps', version: '1.0.3'
    androidTestCompile group: 'info.cukes', name: 'gherkin', version: '2.12.2'
    androidTestCompile 'info.cukes:cucumber-android:1.2.0@jar'
    androidTestCompile group: 'info.cukes', name: 'cucumber-junit', version: '1.2.2'

    it must be that exact version in all cucumber libraries, the latest version doesn't work due to they are compiled with java8, and java8 is not supported yet in android (soon, soon, we hope)

     
  3. We need to setup in gradle that the tests will be executed using Cucumber. Add the instrumentation runner in build.gradle, in the defaultConfig setup

    defaultConfig {
        applicationId "my.application.package"    minSdkVersion 14
        targetSdkVersion 24
        versionCode 1
        versionName "1.0"    
        testInstrumentationRunner "cucumber.api.android.CucumberInstrumentation"
    }
     
  4. Create a new assets folder for the cucumber features in src/androidTest/assets
     
  5. This new folder is not recognized by gradle yet, set the new assets folder in build.gradle exactly, e.g. below the defaultConfig

    defaultConfig {
        ...
    }
    sourceSets {
        androidTest {
            assets.srcDirs = ['src/androidTest/assets']
        }
    }
     
  6. Create a new folder named "features" in the assets folder. All the feature files will stored be here (you can use subfolders). Example of a feature, can be on any file, cucumber doesn't care about the name of the file:

    Feature: Calculator
    
      Scenario Outline: a simple sum
        Given I have numbers 2 and 3
        When I sum
        Then the result is 5
     
  7. This is an important step. Create your tests in the subpackage "test" of your main package id, e.g. my.application.package.test

    A test example for the previous feature:

    @CucumberOptions(features = "features")
    public class CalculatorFeatureTest {
    
        private int mNum1;
        private int mNum2;
        private int mResult;
    
        @Given("^I have numbers (-*\\d+) and (-*\\d+)$")
        public void i_have_numbers_and(int n1, int n2) throws Throwable {
            mNum1 = n1;
            mNum2 = n2;
        }
    
        @When("^I sum$")
        public void i_sum() throws Throwable {
            mResult = mNum1 + mNum2;
        }
    
        @Then("^the result is (-*\\d+)$")
        public void the_result_is(int result) throws Throwable {
            assertEquals(mResult, result);
        }
    
    }

    The annotation @CucumberOptions specifies the folder inside the assets for all the feature files, in this case "features"
     
  8. Right click the package with the tests, and choose "Run tests in ..."
     
  9. You are done







Saturday, July 2, 2016

Be careful, Kotlin includes dead code in generated bytecode.



I enjoyed two Kotlin presentations. Both combined describe the best of this new language.






There is one thing not covered by those presentations, it is a tiny detail in how Kotlin generates the bytecode:

Kotlin inlined functions include dead code in the generated bytecode.



Not a big issue when using Proguard or similar tool to optimize/shrink the classes, but in in case of Android development sometimes is needed to avoid proguard optimization/shrink due to the complexity of the project, libraries, etc.

An example showing code and bytecode in Java and Kotlin


Let's see an example.  We want to log debug messages, but we dont want to include code for debugging purposes in the bytecode of the release build:
  • In java we call a log method inside a condition checking for a boolean constant, so the compiler ignores the code in the release build when the constant is false.
  • In Kotlin we use the advantage of inlined functions, we dont need to always include the condition when calling the log method. The function to log the message is the following

    inline fun debug(func: () -> String) {
        if (BuildConfig.DEBUG) {
            println(func())
        }
    }


We log the message inside a method named "doSomething"

Java
void doSomething() {
    if (BuildConfig.DEBUG) {
        System.out.println("This is a debug message");
    }
}

Kotlin
fun doSomething() {
    debug { "This is a debug message" }
}

+1 for Kotlin, cleaner code.

In java to have a cleaner code we could create a static utility method named "debug" and put the condition inside, but the compiler will include all the calls to "debug" in the bytecode of the release build. Even after optimizing with proguard the method calls will be removed, but not the parameters, depending on how many optimization passes we define in proguard.properties


Let's check now the bytecode generated by Java and Kotlin compilers.


To analyze the bytecode I use the plugin for IntelliJ/Eclipse made by the creators of the ASM library, the OW2 Consortium. I could use the Kotlin plugin included in IntelliJ in menu Tools -> Kotlin -> Show Kotlin Bytecode but it doesnt add an option to ignore the line numbers, unused labels and stack information.


Generated bytecode when DEBUG constant is true

Java
  void doSomething() {
    getstatic 'BuildConfig.DEBUG','Z'
    ifeq l0
    getstatic 'java/lang/System.out','Ljava/io/PrintStream;'
    ldc "This is a debug message"
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
   l0
    return
  }


Kotlin (useless bytecode is underlined)
  public final static void doSomething() {
    nop
    getstatic 'BuildConfig.DEBUG','Z'
    ifeq l0
    ldc "This is a debug message"
    astore 0
    nop
    getstatic 'java/lang/System.out','Ljava/io/PrintStream;'
    aload 0
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V
   l0
    return
  }

+1 for Java.

Why? The difference is small but Kotlin includes some useless nops and the loading of the string "This is a debug message" is a bit dumb, loads the string, stores it, and loads it again.



Generated bytecode when DEBUG constant is false

Java
  void doSomething() {
    return
  }


Kotlin (dead code is underlined)
  public final static void doSomething() {
    nop
    iconst_0
    ifeq l0
    ldc "This is a debug message"
    astore 0
    nop
    getstatic 'java/lang/System.out','Ljava/io/PrintStream;'
    aload 0
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V
   l0
    return
  }

+1 for Java, the java compiler ignores the code inside the condition, but Kotlin does a copy/paste of the code when inlining the function, not taking into account the value of the constant is always false.


A better approach using gradle flavors


What I recommend to do for Kotlin is, instead of checking for a constant, to use gradle flavors, the debug method in the flavor for the debug build shows the message, the debug method in the flavor for the release build does nothing.

Debug flavor Release flavor
inline fun debug(func: () -> String) {
    println(func())
}
inline fun debug(func: () -> String) {
}


Using it in this way we produce clean code and clean bytecode.


Kotlin Bytecode: Debug flavor
  public final static void doSomething() {
    nop
    ldc "This is a debug message"
    astore 0
    nop
    getstatic 'java/lang/System.out','Ljava/io/PrintStream;'
    aload 0
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V
    return
  }

Kotlin Bytecode: Release flavor
  public final static void doSomething() {    
    nop
    return
  }



Kotlin adds a useless nop in the bytecode generated by the release flavor but is clean enough for our needs.


Are we solving a big issue here? Not really, but these small details help us to create clean code without adding an overhead in the compiled classes.