When I started developing Android apps, I didn’t write any tests. Of course not — I was just mucking about, making things for my own enjoyment. Then, I decided to be a professional. I started small, with the simplest possible app, and the simplest possible test, and was immediately stumped. For anyone in the same boat, this article is for you!

Say you have an activity with a button. When you press the button, you want to open a different activity.

Your source code might look something like this:

class ExampleActivity : AppCompatActivity() {
	    fun openOtherActivity() {
	        val intent = Intent(this, OtherActivity::class.java)
	        startActivity(intent)
	   }
	}

Somewhere, in a layout file, you set the onClickof a button to openOtherActivity. But let’s just focus on the Kotlin code. No need to aim too high, right?

Then (or preferably beforehand, if you are diligently applying TDD) you try to write a test. To start with, you might not even check that it behaves as you expect. The simplest possible test is just “When you call the function, nothing explodes.”

It might look something like this:

class ExampleActivityTest {
	    @Test
	    fun breaks() {
	        val exampleActivity = ExampleActivity()
	        exampleActivity.openOtherActivity()
	    }
	}

Even this very simple test, however, results in an error:

java.lang.RuntimeException: Method startActivity in android.app.Activity not mocked.

This is because in test environments, Android-specific stuff doesn’t exist. This was a deliberate design choice by Android, and it has its pros and cons. One outcome of this decision for unit testing is that we have to mock Android-specific components.

One mocking library for Kotlin is MockK, which I will use for this example. Any other similar mocking library should do the trick.

So, back to our test. We can mock the startActivity method (and keep everything else as the real thing) using spykfrom MockK:

class ExampleActivityTest {
	    @Test
	    fun does_not_break() {
	        val exampleActivity = spyk(ExampleActivity())
	        every { exampleActivity.startActivity(any()) } returns Unit
	        exampleActivity.openOtherActivity()
	    }
	}

And now the test passes!

From there, we could extend the test to more thoroughly check the actual behaviour of the function. For example, whether it calls startActivitywith the right kind of intent.

For the next example, suppose we want to add some extras to the intent before opening the activity:

class ExampleActivity : AppCompatActivity() {
	    fun openOtherActivityWithExtras() {
	        val intent = Intent(this, OtherActivity::class.java)
	        intent.putExtra("name", "value")
	        startActivity(intent)
	    }
	}

We try running a similar simple test, just checking that nothing explodes:

class ExampleActivityTest {
	    @Test
	    fun breaks_again() {
	        val exampleActivity = spyk(ExampleActivity())
	        every { exampleActivity.startActivity(any()) } returns Unit
	        exampleActivity.openOtherActivityWithExtras()
	    }
	}

We’ve used the same mocking trick as last time, but now we get another error:

java.lang.RuntimeException: Method putExtra in android.content.Intent not mocked.

#andoid #dependency-injection #kotlin #programming #mobile #app

How to Test Android Activities
17.90 GEEK