5

I'm using Mockito to test my Kotlin code. It's a web app and I use spring to inject values into some fields.

For example, my class snippet looks something like this:

class MyComponent {

    @Inject private lateinit var request: HttpServletRequest
    @Inject private lateinit var database: Database

To mimic this in my unit test I use the @Mock and @InjectMocks annotations from Mockito. So my test looks something like this:

class MyComponentTest {

    @Mock private lateinit var request: HttpServletRequest
    @Mock private lateinit var database: Database

    @InjectMocks private lateinit var sut: MyComponent

    @Before
    fun setup() {
       MockitoAnnotations.initMocks(this)
    }

Which all works fine. However, I also have a lazy initialization block in my component like this:

val user: User by lazy {
    database.findUser()
}

fun getUsername(): String {
    return user.name
}

When my test calls myComponent.getUsername() I would expect database.findUser() to be called as it initializes user but this doesn't happen.

If I put a breakpoint in the lazy block, it's never hit. Now I am assuming this is something to do with the way Mockito and @InjectMocks must 'touch' user but I don't really know. If I construct MyComponent manually then the lazy block is executed - but this won't inject my mocks.

How can I ensure the lazy block is called correctly from my test?

UPDATE: After a week absence, attempting to reproduce this without any changes and I could not. Can't explain it.

Jumwah
  • 517
  • 5
  • 19
  • It would help if you could provide an [MCVE](http://stackoverflow.com/help/mcve). As it is right now I cannot copy your code and reproduce the issue you are seeing. – mfulton26 Apr 05 '16 at 16:24

1 Answers1

3

I've tried to reproduce your problem and was not able to do so. This gist provides a working example.

However I would recommend revisiting the way you write tests. Consider following example:

class MyComponentTest {

    val request = mock<HttpServletRequest>()
    val database = mock<Database>()
    val sut = MyComponent(request, database)

    @Test
    fun username() {
        Mockito.`when`(database.findUser()).thenReturn(User("test"))

        val username = sut.getUsername()

        MatcherAssert.assertThat(username, Matchers.equalTo("test"))
    }
}

Which in my opinion is easier to understand than the one in mentioned the gist.

In case you're interested the mock helper function is a one liner:

inline fun <reified T : Any> mock() = Mockito.mock(T::class.java)

A full updated example can be found in this gist.

miensol
  • 39,733
  • 7
  • 116
  • 112
  • Thanks for the suggestion to use constructor injection, I like this as it means I can get rid of my lateinits and use vals instead of vars. As for the original problem I'll update my question with a MCVE, weird though as your gist is pretty much exactly it. – Jumwah Apr 06 '16 at 13:20
  • OK, I came back to this after a week holiday and tried to reproduce my problem without luck. Something very weird was happening, but my original code with no changes now works. Thanks for you efforts! – Jumwah Apr 15 '16 at 04:17
  • I want to mention that the gist mentioned in the answer has already been changed to lateinits annotation style which, in my opinion, is more clear. – fangzhzh Jun 08 '17 at 21:35