Skip to main content

How to setup RetroLambda, Roboletric and RxAndroid together in Android Studio

Testing environment

Before you proceed the process, Java 1.8 should be installed first in your system.

Here is my testing system:

OS: MacOSX Yosemite(Version 10.10.3)
Android Studio: Version 1.2
Java SDK: Version 1.8.0_45

Project structure

/Project
    /app
        /src
            /main
                /java
            AndroidManifest.xml
            /testDebug
                /java
        build.gradle
    /build
    /gradle
    build.gradle

Configuration /Project/build.gradle

Add dependencies for retrolambda and robolectric plugins under dependencies block.
buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:1.2.2'
        classpath 'me.tatarka:gradle-retrolambda:3.1.0'
        classpath 'org.robolectric:robolectric-gradle-plugin:1.0.1'
    }
}

allprojects {
    repositories {
        jcenter()
    }
}

Configuration /Project/app/build.gradle

apply plugin: 'com.android.application'
apply plugin: 'me.tatarka.retrolambda'
apply plugin: 'org.robolectric'

android {
    compileSdkVersion 22
    buildToolsVersion "22.0.1"

    defaultConfig {
        applicationId "com.spark.example.rxjava"
        minSdkVersion 10
        targetSdkVersion 22
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:22.1.1'
    compile 'io.reactivex:rxandroid:0.24.0'

    testCompile 'junit:junit:4.12'
    testCompile 'org.robolectric:robolectric:2.4'
}

robolectric {
    // Configure includes / excludes
    include '**/*Test.class'
    exclude '**/espresso/**/*.class'

    // Configure max heap size of the test JVM
    maxHeapSize = '2048m'

    // Configure the test JVM arguments - Does not apply to Java 8
    jvmArgs '-XX:MaxPermSize=512m', '-XX:-UseSplitVerifier'

    // Specify max number of processes (default is 1)
    maxParallelForks = 4

    // Specify max number of test classes to execute in a test process
    // before restarting the process (default is unlimited)
    forkEvery = 150

    // configure whether failing tests should fail the build
    ignoreFailures true

    // use afterTest to listen to the test execution results
    afterTest { descriptor, result ->
        println "Executing test for ${descriptor.name} with result: ${result.resultType}"
    }
}

Simple unit test

package com.spark.example.rxjava;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;

import java.util.concurrent.CountDownLatch;

import rx.Observable;
import rx.Observer;
import rx.Subscriber;
import rx.functions.Action1;

import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;

@Config(emulateSdk = 18, manifest = "app/src/main/AndroidManifest.xml")
@RunWith(RobolectricTestRunner.class)
public class SimpleTest {

    private static final String TEST_STR = "Hello, RxAndroid";

    private String actual;
    private CountDownLatch latch;

    @Before
    public void setUp() throws Exception {
        latch = new CountDownLatch(1);
    }

    @Test
    public void testHelloRxAndroid() throws Exception {
        Observable helloObservable = Observable.create(subscriber -> handleObserver(subscriber));
        helloObservable.subscribe(new HelloSubscriber());

        latch.await();
        assertThat(actual, is(TEST_STR));
    }

    @Test
    public void testJustHello() throws InterruptedException {
        Observable justObservable = Observable.just(TEST_STR);
        justObservable.subscribe(new HelloSubscriber());

        latch.await();
        assertThat(actual, is(TEST_STR));
    }

    @Test
    public void testWithAction() throws InterruptedException {
        Observable observable = Observable.create(subscriber -> handleObserver(subscriber));

        observable.subscribe(s -> {
            actual = s;
            latch.countDown();
        });

        latch.await();
        assertThat(actual, is(TEST_STR));
    }

    private void handleObserver(Subscriber subscriber) {
        subscriber.onNext(TEST_STR);
        subscriber.onCompleted();
    }

    private class HelloSubscriber extends Subscriber {

        @Override
        public void onCompleted() {
            latch.countDown();
        }

        @Override
        public void onError(Throwable e) {
            actual = "onError";
            latch.countDown();
        }

        @Override
        public void onNext(String s) {
            actual = s;
        }
    }
}

I think Lambda is one of quite useful features Java 8 supports. You can easily compare both and guess the Lambda's benefit.

No Lambda
observable.subscribe(new Action1() {
            @Override
            public void call(String s) {
                actual = s;
                latch.countDown();
            }
        });
Lambda
observable.subscribe(s -> {
            actual = s;
            latch.countDown();
        });

Comments

Popular posts from this blog

Apply Kotlin DataBinding to Android Studio Generated Main Activity

I posted how to setup Kotlin and DataBinding in Android Stuido in the last blog (http://marksunghunpark.blogspot.com.au/2017/04/kotlin-binding-library-setup-in-android.html). In this post, I am going to how to use DataBiding in the MainActivity when you create a navigation drawer project from Android Studio templates. Layouts You will have four layouts in app/src/res/layout folder: app/src/main/res/layout/activity_main.xml app/src/main/res/layout/app_bar_main.xml app/src/main/res/layout/content_main.xml app/src/main/res/layout/nav_header_main.xml And activity_main.xml contains all other layout using include layout. You need to have tag in activity_main.xml , app_bar_main.xml and content_main.xml . If you don't have the tag, Binding library cannot recognise the sub layouts properly. Binding library doesn't support toolbar and navigation drawer yet, so you can use using BindingAdapter if you want to use binding library.(But I'm gong to skip this part for simplici

How to test AsyncTask in Android

In Android, test is not as easy as any other platform. Because Android test cannot be run without emulator. Particulary when it comes to AsyncTask or Service, it is difficult to test because they are different type of thread and hard to check their result. Then, how can we ensure the result of AsyncTask valid? AsyncTask is a thread and an asynchnorous as the name means. So, we need to wait for it finishes its job and need to capture the event. Then, when it happens in AsyncTask. It can be one of onBackground() and onPostExecute() methods. It doesn't matter you use onBackground() or onPostExecute() but I prefer onPostExecute(). Anyway, we can test an AsyncTask if we can hook it. Then, how can we hook it? For that, we can use callback pattern. But we need to delay main thread to wait for the AsyncTask's job done because we want to check the result. So the structure for the test would be like: 1. Create AsyncTask A 2. Injection a callback into A 3. Wait until A finish 4.

Let's start Lambda in Android

In Android programming, I think Java & J2EE programming are not that different, we developers put many boiler plate code particularly when we use anonymous instances. Example1. Simple example button1.setOnClickListener(new OnClickListener(){ @Override public void onClick(View view){ //Do something } }); In the above example, what we really need is inside of onClick() method. Other code is actually decorations and we don't want it. How about we can pass just onClick method body as a parameter of setOnClickListener method like below? button1.setOnClickListener(view->{ //Do something }); That's what we have exactly wanted. We can use the code style in Java 8. I think you already might know about Java 8 Lambda but there is no official support for Java 8 in Android 8. It would be awesome we can use it in Android and there is a way to be able to use Lambda in Android as well. You can refer my previous blog to setup RetroLambda in Andro