16

I'm trying to initialize (by mocking) two objects with the annotation @MockBean

It seems only to work if i call the method mock(className), but since i want to use the mocked class on multiple methods i don't want to keep repeating the same code in my test methods.

This is my test class:

@RunWith(MockitoJUnitRunner::class)
class WordServiceTest {

    @MockBean
    lateinit var wordRepositoryMock: WordRepository

    @MockBean
    private lateinit var wordMapperMock: WordMapper

    @Test
    fun findAllTest() {
        // Error: lateinit property wordRepositoryMock has not been initialized
        val wordService = WordService(wordRepositoryMock, wordMapperMock)
        `when`(wordRepositoryMock.findAll()).thenReturn(listOf(
                WordEntity(UUID.randomUUID(), "xxx"),
                WordEntity(UUID.randomUUID(), "xxx")))
        assertEquals(2, wordService.findAll().size)
    }

    @Test
    fun wordExistsTest() {
        // This works fine
        val wordRepositoryMock = mock(WordRepository::class.java)
        val wordMapperMock = mock(WordMapper::class.java)
        val wordService = WordService(wordRepositoryMock, wordMapperMock)
        val word = "xxx"
        `when`(wordRepositoryMock.existsWordEntityByName(word)).thenReturn(true)
        assertEquals(true, wordService.wordExists(word))
    }
}

I don't want to use the Spring Boot @Autowired annotation because my spring application requires contexts which i don't want to load.

The error i'm getting:

lateinit property wordRepositoryMock has not been initialized
kotlin.UninitializedPropertyAccessException: lateinit property wordRepositoryMock has not been initialized

Dependencies:

dependencies {
    ...
    testImplementation("org.springframework.security:spring-security-test")
    testImplementation ('org.springframework.boot:spring-boot-starter-test') 
    testImplementation("org.junit.jupiter:junit-jupiter:5.6.2")
    testImplementation "org.junit.jupiter:junit-jupiter-params:5.5.2"
    testImplementation "org.junit.jupiter:junit-jupiter-api:5.6.2"
    testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.6.2"
    testImplementation("io.rest-assured:spring-mock-mvc:4.0.0")
    testImplementation("io.mockk:mockk:1.9.3")
    testImplementation "org.testcontainers:postgresql:1.11.3"
    testImplementation "org.springframework.kafka:spring-kafka-test:2.2.7.RELEASE"
    runtimeOnly('org.postgresql:postgresql')
    developmentOnly 'org.springframework.boot:spring-boot-devtools'
    testImplementation "org.mockito:mockito-junit-jupiter:3.3.3"
}
J. Adam
  • 1,457
  • 3
  • 20
  • 38

3 Answers3

22

I solved this problem by calling initMocks method in setUp()

@BeforeEach
    fun setUp() {
        MockitoAnnotations.openMocks(this)
        ....
    }
Salam El-Banna
  • 3,784
  • 1
  • 22
  • 34
J. Adam
  • 1,457
  • 3
  • 20
  • 38
  • 1
    Unfortunately, I received this message when using this suggestion: "'initMocks(Any!): Unit' is deprecated. Deprecated in Java" – Bruno Carneiro Oct 29 '21 at 19:27
  • In case someone is still wondering, @BrunoCarneiro you got that warning because you were trying to use initMocks instead of openMocks – SoManyGoblins Sep 07 '22 at 08:30
  • initMocks() is deprecated now, you have to use openMocks() as suggested here: https://stackoverflow.com/a/70789353/1820599 – adalpari Oct 24 '22 at 14:04
12

MockitoJUnitRunner works with the @Mock annotation.

@MockBean is coming from Spring so you need to use the @SpringRunner.

However, it looks like your examples don't need Spring, so you can switch to @Mock:

@RunWith(MockitoJUnitRunner::class)
class WordServiceTest {

    @Mock
    private lateinit var wordRepositoryMock: WordRepository

    @Mock
    private lateinit var wordMapperMock: WordMapper
}

There's a further improvement you could make if you upgraded to Junit5 (I only tested it in Junit5). Constructor arguments are a good alternative to lateinit:

@ExtendWith(MockitoExtension::class)
class WordServiceTest(
    @Mock val wordRepositoryMock: WordRepository,
    @Mock val wordMapperMock: WordMapper
) {

}
Jakub Zalas
  • 35,761
  • 9
  • 93
  • 125
  • 1
    Thanks for your answer. Hovering over MockitoExtension::class gives me the following error: ```Type mismatch. Required: KClass Found: KClass``` Which dependencies do you use? – J. Adam Jun 02 '20 at 12:06
  • Sorry, it should be `@ExtendWith(MockitoExtension::class)` – Jakub Zalas Jun 02 '20 at 12:21
  • Ye i figured out that but i'm getting the following error now: ```org.junit.platform.launcher.core.DefaultLauncher handleThrowable WARNING: TestEngine with ID 'junit-jupiter' failed to discover tests``` See updated post for my gradle dependencies – J. Adam Jun 02 '20 at 12:31
  • If you're trying to now upgrade junit, than this has become a totally different problem ;) the `@Test` annotation is in a different package. Be careful not to mix the two. I'd try to make it work with the existing version first (my first example). Not tried it, but perhaps the following would also work? ```@RunWith(MockitoJUnitRunner::class) class WordServiceTest( @Mock val wordRepositoryMock: WordRepository, @Mock val wordMapperMock: WordMapper ) { }``` – Jakub Zalas Jun 02 '20 at 12:52
  • Well i tried both and both give different errors. With the first version i'm getting the error: ```kotlin.UninitializedPropertyAccessException``` using @Test of package: ```import org.junit.jupiter.api.Test``` – J. Adam Jun 02 '20 at 17:08
  • FWIW, it's the c-tor injection that's the real solution here, both for `@Mock` and `@MockBean` - works out of the box, no magic needed – spamove Feb 09 '22 at 00:42
11

For the ones coming because initMocks(this) is deprecated, I noticed there's a newer function that does the same:

@BeforeEach
fun setUp() {
    MockitoAnnotations.openMocks(this)
        ....
}
Eury Pérez Beltré
  • 2,017
  • 20
  • 28