Kotlin - Why is this variable null after initialization?

Viewed 971

I'm trying to write a unit test for some Android code that looks for a specific key being present in an Intent object. As part of my test, I'm creating an Intent object and adding a String to it. However, when I use the below code, my variable is initalized to null:

val data = Intent().putExtra("key", "value")
// data is null

If I split this up into two lines, it works just fine:

val data = Intent()
data.putExtra("key", "value")
// data is non-null and contains my key/value

What feature of the Kotlin language is causing this to happen?


Note that putExtra() returns an Intent object. From the Android source:

public @NonNull Intent putExtra(String name, String value) {
    if (mExtras == null) {
        mExtras = new Bundle();
    }
    mExtras.putString(name, value);
    return this;
}

In the first case, the inferred type is Intent!. I was under the impression that this just means that it's an Intent or an Intent? but Kotlin doesn't want to make devs go crazy with Java platform types. Still, given that putExtra() returns a non-null value, I'd expect the actual value of data to be non-null.

1 Answers

The short answer is what @CommonsWare and @TheWanderer mentioned in comments: my test class was in the test/ directory, so it was using a mock Intent implementation instead of the real thing.

When I move my test to the androidTest/ directory, everything works as expected. The observed behavior has nothing to do with Kotlin.


Some extra info about why this was so confusing...

First, I was mistaken when I wrote this:

val data = Intent()
data.putExtra("key", "value")
// data is non-null and contains my key/value

The data variable was non-null, but it did not actually contain my key/value pair. The mock Intent implementation I was using was dropping the putExtra() call.

So, why was my test passing?

The one particular test I decided to dig deeper on was testing the negative case (when a key other than the one it expects is present in the Intent). But I wasn't passing an Intent with the wrong key, I was passing an Intent with no keys at all. Either way, though, the expected key is not present, and the method returns false.

The positive case (where the required key actually was passed to putExtra()) failed with an AssertionError. Too bad I didn't pick this one to scrutinze.

My main project has apparently stubbed Intent.putExtra() as a no-op, via the returnDefaultValues = true gradle option. When I create a new project and try to reproduce this issue, I get a very clear error:

java.lang.RuntimeException: Method putExtra in android.content.Intent not mocked. See http://g.co/androidstudio/not-mocked for details.
    at android.content.Intent.putExtra(Intent.java)
    at com.example.stackoverflow.IntentTest.test(IntentTest.kt:12)

Unfortunately, with the mocked putExtra(), I never got this helpful message.

Related