我正在使用EasyMock与Kotlin。有一个示例类,我尝试模仿。
我不断收到的问题是,anyObject
,无论是否有特定的类,都会抛出NullPointerException
,因为Kotlin在类型方面比Java更严格。
java.lang.NullPointerException:anyObject(Logger::class.java)不能为null
下面是我使用简单类实现运行的测试示例。
基于EasyMock的测试MyServiceMockTest.kt:
import org.easymock.EasyMock.anyObject
import org.easymock.EasyMock.anyString
import org.easymock.EasyMock.replay
import org.easymock.EasyMock.verify
import org.easymock.EasyMockExtension
import org.easymock.Mock
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.slf4j.Logger
@ExtendWith(EasyMockExtension::class)
class MyServiceMockTest {
@Mock
private lateinit var loggerService: LoggerService
private lateinit var myService: MyService
@BeforeEach
fun setUp() {
myService = MyService(loggerService)
}
@Test
fun `should test logger implementation`() {
loggerService.info(anyObject(Logger::class.java), anyString())
replay(loggerService)
myService.`generate different logs based on incoming numbers`(0)
verify(loggerService)
}
}
字符串
MyService.kt:
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service
@Service
class MyService @Autowired constructor(val loggerService: LoggerService) {
private val logger = LoggerFactory.getLogger(MyService::class.java)
companion object {
const val BREADCRUMB_ID = "fd8f6ac2-8d27-11ee-b9d1-0242ac120002"
}
fun `generate different logs based on incoming numbers`(num: Int) {
when (num) {
0 ->
loggerService.info(
logger = logger,
breadcrumbId = BREADCRUMB_ID
)
1 ->
loggerService.info(
logger = logger,
breadcrumbId = BREADCRUMB_ID,
events = listOf("Log a single message")
)
2 ->
loggerService.info(
logger = logger,
breadcrumbId = BREADCRUMB_ID,
events = listOf("Log a message"),
params = mapOf("num" to num)
)
3 ->
loggerService.warn(
logger = logger,
breadcrumbId = BREADCRUMB_ID,
params = mapOf("num warnings" to num)
)
else -> {
loggerService.info(
logger = logger,
breadcrumbId = BREADCRUMB_ID
)
loggerService.warn(
logger = logger,
breadcrumbId = BREADCRUMB_ID
)
loggerService.error(
logger = logger,
breadcrumbId = BREADCRUMB_ID
)
}
}
}
}
型
LoggerService.kt:
import org.slf4j.Logger
import org.springframework.boot.logging.LogLevel
import org.springframework.boot.logging.LogLevel.ERROR
import org.springframework.boot.logging.LogLevel.INFO
import org.springframework.boot.logging.LogLevel.WARN
import org.springframework.stereotype.Service
/**
* As this LoggerService unique per service,
* be it one of the services in a monolith
* or one of isolated services in microservice architecture
* it has an extra field <code>breadcrumbId</code>, so
* all the messages can be traced by this ID.
*/
@Service
open class LoggerService {
fun info(logger: Logger, breadcrumbId: String, events: List<Any>? = null, params: Map<Any, Any?>? = null) {
log(INFO, logger, breadcrumbId, events, params)
}
fun error(logger: Logger?, breadcrumbId: String?, events: List<Any>? = null, params: Map<Any, Any?>? = null) {
log(ERROR, logger!!, breadcrumbId!!, events, params)
}
fun warn(logger: Logger, breadcrumbId: String, events: List<Any>? = null, params: Map<Any, Any?>? = null) {
log(WARN, logger, breadcrumbId, events, params)
}
/**
* The implementation is limited, it does not include TRACE, DEBUG modes.
*/
private fun log(level: LogLevel, logger: Logger, breadcrumbId: String, events: List<Any>? = null, params: Map<Any, Any?>? = null) {
/**
* Builder behavior mimics MDC Logger.
* It allows to have a greater flexibility, than some other available solutions.
*/
val message = LogMessage(breadcrumbId)
.addParams("events", events)
.addParams("params", params)
.build()
when (level) {
ERROR -> logger.error(message)
WARN -> logger.warn(message)
else -> logger.info(message)
}
}
}
/**
* The implementation of this class can be further extended.
*
* Here is a simple reference implementation that can be used as it is.
*/
open class LogMessage {
private val builder: StringBuilder
constructor(breadcrumbId: String){
builder = StringBuilder("[$breadcrumbId]")
}
fun addParams(name: Any, value: Any?): LogMessage {
if (value != null) {
builder.append(", $name: $value")
}
return this
}
fun build(): String {
return builder.toString()
}
}
型
我以前在anyString
上也遇到过类似的问题,但是通过anyString
的函数扩展解决了这个问题,但是对于anyObject
,我无法提出类似的实现。
当前的测试是否有任何方法可以在没有NullPointerException
的情况下使用函数或其他替代方案?
1条答案
按热度按时间oxf4rvwz1#
这是一个有趣的问题。因为Kotlin不能接受传递给一个不能接收null的方法的null,所以它会发疯。我做了一些阅读和实验。似乎唯一的方法就是这样做。
字符串
和
型
在本例中,
anyString
和anyObject
不再返回null
。型
因为你不能在匹配器中创建mock。这让EasyMock抓狂。
可悲的是,当使用EasyMock 5.2.0和Java 21执行此操作时,mock下的拦截器似乎没有正常工作。我们得到了一个
java.lang.IllegalStateException: matcher calls were used outside expectations
。我不知道为什么,但很可能是因为Kotlin为LoggerService
生成了一个奇怪的类。如果你有相同的结果,你可以提交一个EasyMock bug。