Matt's Tidbits #50 - How to (inadvertently) initialize a Kotlin val twice
Tags: mattstidbits, android, kotlin, testing
Published: 2020-01-07 15:01:32

[Last week I wrote about how to resolve a bug where the UI of Android Studio's Logcat window is incomplete.](https://medium.com/@matthew.b.groves/matts-tidbits-49-what-to-do-when-your-log-cat-misbehaves-7c3513a78a58) This time, I have a word of caution when initializing mocks in Kotlin unit tests.

If you've worked with Mockito before, you've probably written code like this:
{% gist https://gist.github.com/mpeng3/45d289878060566c594b17f62728aa66 %}

In this example, we have an interface that we want to mock, and we're initializing setting it up to return a specific value (5), and we want to verify when we `callStaticObjectToGetNum()` that it performs whatever calculations it needs to, but eventually returns the expected mock value to us.

Unfortunately, if you run this test, you will get the following output:
```
java.lang.AssertionError: 
Expected: is <5>
     but: was <0>
Expected :is <5>
Actual   :<0>
```

What is going on here? The key is on lines 17 & 18. What is happening in this instance is that, despite the `val` keyword, our mock is actually getting initialized twice due to having both the `@Mock` and `Mockito.mock()` calls associated with it.

In short, the `val` is assigned one mock instance initially, and that is passed to `passMockToSomeStaticObject()`. This happens at class load time. Then, immediately before the test is run, the `MockitoAnnotations.initMocks()` call processes the `@Mock` associated with this variable and generates a **_new mock instance_**. Therefore, when we set up our mock to return 5 for the `getNum()` method on line 32, we're setting this on the 2nd instance. However, when we call `callStaticObjectToGetNum()` on line 34, it's pointing at the 1st instance - therefore, the test properly reports that the actual value was 0, even though we were expecting 5.

How do we fix this? We have multiple options, and fortunately some of them are easy:
1. The simplest option is to remove the `@Mock` annotation from line 17 - this will remove the double instantiation, guaranteeing that we're always referring to the same mock instance.
2. It's also an option to remove the `Mockito.mock(MyIfc::class.java)` call and just use `@Mock` instead. However, this option requires some additional refactoring, as we'll need to make this a `lateinit var` instead of a `val`, and also move the call to `passMockToSomeStaticObject()` into the `@Before` method after we've called `initMocks()` so we guarantee the "static object" refers to the correct value (and it is non-null).

That's all there is to it! I ran into this about a year ago and it was so simple, and yet so painful to debug that I wanted to share my story and hopefully prevent someone else from falling into the same trap.

Before I wrap up, I have two other things to point out - working with Mockito in Kotlin can be a little difficult as the `when` and `is` methods are both reserved keywords. There are libraries such as Mockito-kotlin that help rewrite these methods, but you can also achieve the same thing yourself by using the `as` keyword and isolating the really ugly "`" syntax to the import statement - see lines 2 and 8 in the example above.

How else would you work around this Mock initialization issue in your code? Leave me a comment below! And, please follow me on Medium if you're interested in being notified of future tidbits.

Interested in joining the awesome team here at Intrepid? [We're hiring!](https://www.intrepid.io/careers)

*This tidbit was originally delivered on November 2, 2018.*