Android Unit Testing:
Unit Testing is considered as the most professional and disciplined way of writing an elegant code across the software industry. Yet even many developers tend to skip writing unit tests as they are time consuming. When properly written, unit tests provide lots of advantages and helps reduce the build generation times to a great extent in the long run. Same applies to Android Unit testing too.
This article is focused on giving an overview about Android unit testing advantages and also How to write a simple Android Unit Test using Robolectric library.
The link to download the complete code for this android unit testing tutorial is given at the bottom of the article.
Advantages of Android Unit Testing:
- The main Advantage of Android Unit testing is that it helps creating faster builds with new production code along with peace of mind, as we know that the existing logic works as it is.
- Android unit testing helps test the code without running on emulator and hence its much faster (matter of seconds!)
- Less debugging as we can know which unit test has failed and check the respective code written to make the respective unit test to pass.
Three Laws of TDD (Test Driven Development):
Uncle Bob has explained the three laws of Test Driven Development in a very efficient manner in the below video:
In brief, the three laws described in the video are as follows:
- You are not allowed to write any production code unless it is to make a failing unit test pass.
- You are not allowed to write any more of a unit test than is sufficient to fail; and compilation failures are failures.
- You are not allowed to write any more production code than is sufficient to pass the one failing unit test.
Keeping in mind these above 3 laws and developing test cases would help us write better unit testing for Android Development.
Now lets straight ahead dive into writing a simple Android Unit test using Robolectric.
Android Unit Testing using Robolectric:
In Google I/0 2017 conference, Google developers made a statement that they were continuously contributing to Robolectric, an open source unit test framework, to help efficiently write unit tests for Android.
Why Robolectric?
Robolectric library contains many mocks of Android classes unlike Mockito, another framework which contains mocks of classes. The Robolectric test runner injects these ‘shadow objects’ in place of the actual Android classes when the tests are run. This is what allows the tests to run on the JVM without booting up an instance of Android.
Integration of Robolectric into Android Studio:
In this article, our goal is simple:
“To add a unit test and make it pass”
Here we will be adding a test case for a textview to match some text upon clicking a button. So lets start adding our Unit test.
Steps to perform Android Unit testing:
1. Integrate Robolectric into Android studio
To integrate Robolectric library into our Android Studio project, lets add the following code in our app module’s build.gradle file:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
dependencies { | |
… | |
// Robolectric | |
testImplementation "org.robolectric:robolectric:3.7.1" | |
… | |
} |
and also the following code to the same file in android{ } block:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
android { | |
… | |
testOptions { | |
unitTests { | |
includeAndroidResources = true | |
} | |
} | |
… | |
} |
2. Writing a unit test
So, following Uncle bob’s first law, let us first write a unit test following the production code to simply make the test case pass.
The test cases are usually under the following folder:
src -> test -> <package_name>
Here I am creating a new file named SimpleUnitTest.kt and write the following code to it:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.coderefer.androidtestingexamples | |
import kotlinx.android.synthetic.main.activity_main.* | |
import org.junit.Test | |
import org.junit.Assert.* | |
import org.junit.runner.RunWith | |
import org.robolectric.Robolectric | |
import org.robolectric.RobolectricTestRunner | |
/** | |
* Simple local unit test, which will execute on the development machine (host). | |
* | |
* See [testing documentation](http://d.android.com/tools/testing). | |
*/ | |
@RunWith(RobolectricTestRunner::class) | |
class SimpleUnitTest { | |
@Test | |
fun buttonText(){ | |
var activity = Robolectric.setupActivity(MainActivity::class.java) | |
activity.button.performClick() | |
assertEquals(activity.textView.text,"hello") | |
} | |
} |
If you’ve observed, we’ve used some annotations in the above code:
- Every class that needs to use Roboelectric must be annotated with @RunWith(RobolectricTestRunner::class)  annotation.
- Every test function must be annotated with @Test annotation
Now lets run the test case to see that it fails.
So our job is simple:Â to make the above test case pass by writing some production code. So let’s write the required code in our MainActivity.kt and make our test case pass.
3. Creating a layout file:
Next, I am modifying the layout file such that it contains a button and a textview.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<android.support.constraint.ConstraintLayout | |
xmlns:android="http://schemas.android.com/apk/res/android" | |
xmlns:app="http://schemas.android.com/apk/res-auto" | |
xmlns:tools="http://schemas.android.com/tools" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" | |
tools:context="com.coderefer.androidtestingexamples.MainActivity"> | |
<Button | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:text="click me!" | |
android:id="@+id/button" | |
app:layout_constraintBottom_toBottomOf="parent" | |
app:layout_constraintLeft_toLeftOf="parent" | |
app:layout_constraintRight_toRightOf="parent" | |
app:layout_constraintTop_toTopOf="parent"/> | |
<TextView | |
android:id="@+id/textView" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:layout_marginBottom="8dp" | |
android:layout_marginTop="8dp" | |
android:text="TextView" | |
app:layout_constraintBottom_toTopOf="@+id/button" | |
app:layout_constraintLeft_toLeftOf="parent" | |
app:layout_constraintRight_toRightOf="parent" | |
app:layout_constraintTop_toTopOf="parent"/> | |
</android.support.constraint.ConstraintLayout> |
4. Modifying our Main Activity:
Lets tweak our main activity slightly such that upon button click, textview gets appended with “hello” text.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.coderefer.androidtestingexamples | |
import android.support.v7.app.AppCompatActivity | |
import android.os.Bundle | |
import kotlinx.android.synthetic.main.activity_main.* | |
class MainActivity : AppCompatActivity() { | |
override fun onCreate(savedInstanceState: Bundle?) { | |
super.onCreate(savedInstanceState) | |
setContentView(R.layout.activity_main) | |
button.setOnClickListener({ | |
textView.text = "hello" | |
}) | |
} | |
} |
This is our example production code. To make test case pass, we are appending “hello” to textview upon clicking button in our MainActivity.
5. Run the test
Now that we’ve integrated production code lets run the test case again.
As shown in the above image, now the test case has been passed.
In this way we would be writing unit test case followed by production code for each and every business logic.
The complete code for the above article can be downloaded from the link below:
[emaillocker]
https://github.com/vamsitallapudi/AndroidTestingExamples
[/emaillocker]
In the Next articles, we would be discussing about more compex unit tests, few other terminologies and concepts, medium and large tests. Stay tuned for the updates in near future!