mockito / mockito-kotlin

Using Mockito with Kotlin
MIT License
3.09k stars 198 forks source link

java.lang.NullPointerException when upgraded to kotlin 2.0 #519

Open hanrw opened 2 weeks ago

hanrw commented 2 weeks ago

gradle config

plugins {
    id("org.springframework.boot") version "3.3.0"
    id("io.spring.dependency-management") version "1.1.5"
    kotlin("jvm") version "2.0.0"
    kotlin("plugin.spring") version "2.0.0"
}

group = "com.kotlin.npe"
version = "0.0.1-SNAPSHOT"

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(21)
    }
}

repositories {
    mavenLocal()

    mavenCentral()
}

dependencies {
    implementation("org.springframework.boot:spring-boot-starter")
    implementation("org.springframework.boot:spring-boot-starter-data-jpa")
    testImplementation("org.springframework.boot:spring-boot-starter-test")
    testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")
    testImplementation("org.mockito.kotlin:mockito-kotlin:5.3.1")
    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}

kotlin {
    compilerOptions {
        freeCompilerArgs.addAll("-Xjsr305=strict")
    }
}

tasks.withType<Test> {
    useJUnitPlatform()
}

test

@ExtendWith(MockitoExtension::class)
class UserStatusServiceTest {
    @InjectMocks
    lateinit var target: UserStatusServiceImpl

    @Mock
    lateinit var userStatusRepository: UserStatusRepository

    @Test
    fun addStatus() {
        whenever(
            userStatusRepository.findByUserId(
                1L,
            )
        ).thenReturn(
            Optional.empty()
        )

        target.addStatus(1L)

        val suspension = UserStatus()
        verify(userStatusRepository).save(suspension)
    }
}

service

interface UserStatusService {
    fun addStatus(userId: Long)
}

class UserStatusServiceImpl(private val userStatusRepository: UserStatusRepository) :
    UserStatusService {
    override fun addStatus(userId: Long) {
        val optional = userStatusRepository.findByUserId(userId)
        if (optional.isEmpty) {
            println("[userId: $userId] adding a new status")

            userStatusRepository.save(UserStatus())
        } else {
            println("[userId: $userId] status already exists")
        }
    }
}

repository

@Repository
interface UserStatusRepository : JpaRepository<UserStatus, Long>,
    JpaSpecificationExecutor<UserStatus> {
    fun findByUserId(userId: Long): Optional<UserStatus>
}

data class UserStatus(
    val userId: Long = 1L
)

and got error

WARNING: If a serviceability tool is in use, please run with -XX:+EnableDynamicAgentLoading to hide this warning
WARNING: If a serviceability tool is not in use, please run with -Djdk.instrument.traceUsage for more information
WARNING: Dynamic loading of agents will be disallowed by default in a future release
[userId: 1] adding a new status

java.lang.NullPointerException
    at com.kotlin.npe.reporduce.UserStatusServiceImpl.addStatus(UserStatusService.kt:11)
    at com.kotlin.npe.reporduce.UserStatusServiceTest.addStatus(UserStatusServiceTest.kt:31)
    at java.base/java.lang.reflect.Method.invoke(Method.java:580)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)

here's reproducing repo and it works after downgrading to kotlin 1.9.24 archive.zip