Spring Boot 在Kotlin中使用EasyMock时,anyObject()不能为null

6za6bjd0  于 2024-01-06  发布在  Spring
关注(0)|答案(1)|浏览(115)

我正在使用EasyMock与Kotlin。有一个示例类,我尝试模仿。
我不断收到的问题是,anyObject,无论是否有特定的类,都会抛出NullPointerException,因为Kotlin在类型方面比Java更严格。
java.lang.NullPointerException:anyObject(Logger::class.java)不能为null
下面是我使用简单类实现运行的测试示例。
基于EasyMock的测试MyServiceMockTest.kt:

  1. import org.easymock.EasyMock.anyObject
  2. import org.easymock.EasyMock.anyString
  3. import org.easymock.EasyMock.replay
  4. import org.easymock.EasyMock.verify
  5. import org.easymock.EasyMockExtension
  6. import org.easymock.Mock
  7. import org.junit.jupiter.api.BeforeEach
  8. import org.junit.jupiter.api.Test
  9. import org.junit.jupiter.api.extension.ExtendWith
  10. import org.slf4j.Logger
  11. @ExtendWith(EasyMockExtension::class)
  12. class MyServiceMockTest {
  13. @Mock
  14. private lateinit var loggerService: LoggerService
  15. private lateinit var myService: MyService
  16. @BeforeEach
  17. fun setUp() {
  18. myService = MyService(loggerService)
  19. }
  20. @Test
  21. fun `should test logger implementation`() {
  22. loggerService.info(anyObject(Logger::class.java), anyString())
  23. replay(loggerService)
  24. myService.`generate different logs based on incoming numbers`(0)
  25. verify(loggerService)
  26. }
  27. }

字符串
MyService.kt:

  1. import org.slf4j.LoggerFactory
  2. import org.springframework.beans.factory.annotation.Autowired
  3. import org.springframework.stereotype.Service
  4. @Service
  5. class MyService @Autowired constructor(val loggerService: LoggerService) {
  6. private val logger = LoggerFactory.getLogger(MyService::class.java)
  7. companion object {
  8. const val BREADCRUMB_ID = "fd8f6ac2-8d27-11ee-b9d1-0242ac120002"
  9. }
  10. fun `generate different logs based on incoming numbers`(num: Int) {
  11. when (num) {
  12. 0 ->
  13. loggerService.info(
  14. logger = logger,
  15. breadcrumbId = BREADCRUMB_ID
  16. )
  17. 1 ->
  18. loggerService.info(
  19. logger = logger,
  20. breadcrumbId = BREADCRUMB_ID,
  21. events = listOf("Log a single message")
  22. )
  23. 2 ->
  24. loggerService.info(
  25. logger = logger,
  26. breadcrumbId = BREADCRUMB_ID,
  27. events = listOf("Log a message"),
  28. params = mapOf("num" to num)
  29. )
  30. 3 ->
  31. loggerService.warn(
  32. logger = logger,
  33. breadcrumbId = BREADCRUMB_ID,
  34. params = mapOf("num warnings" to num)
  35. )
  36. else -> {
  37. loggerService.info(
  38. logger = logger,
  39. breadcrumbId = BREADCRUMB_ID
  40. )
  41. loggerService.warn(
  42. logger = logger,
  43. breadcrumbId = BREADCRUMB_ID
  44. )
  45. loggerService.error(
  46. logger = logger,
  47. breadcrumbId = BREADCRUMB_ID
  48. )
  49. }
  50. }
  51. }
  52. }


LoggerService.kt:

  1. import org.slf4j.Logger
  2. import org.springframework.boot.logging.LogLevel
  3. import org.springframework.boot.logging.LogLevel.ERROR
  4. import org.springframework.boot.logging.LogLevel.INFO
  5. import org.springframework.boot.logging.LogLevel.WARN
  6. import org.springframework.stereotype.Service
  7. /**
  8. * As this LoggerService unique per service,
  9. * be it one of the services in a monolith
  10. * or one of isolated services in microservice architecture
  11. * it has an extra field <code>breadcrumbId</code>, so
  12. * all the messages can be traced by this ID.
  13. */
  14. @Service
  15. open class LoggerService {
  16. fun info(logger: Logger, breadcrumbId: String, events: List<Any>? = null, params: Map<Any, Any?>? = null) {
  17. log(INFO, logger, breadcrumbId, events, params)
  18. }
  19. fun error(logger: Logger?, breadcrumbId: String?, events: List<Any>? = null, params: Map<Any, Any?>? = null) {
  20. log(ERROR, logger!!, breadcrumbId!!, events, params)
  21. }
  22. fun warn(logger: Logger, breadcrumbId: String, events: List<Any>? = null, params: Map<Any, Any?>? = null) {
  23. log(WARN, logger, breadcrumbId, events, params)
  24. }
  25. /**
  26. * The implementation is limited, it does not include TRACE, DEBUG modes.
  27. */
  28. private fun log(level: LogLevel, logger: Logger, breadcrumbId: String, events: List<Any>? = null, params: Map<Any, Any?>? = null) {
  29. /**
  30. * Builder behavior mimics MDC Logger.
  31. * It allows to have a greater flexibility, than some other available solutions.
  32. */
  33. val message = LogMessage(breadcrumbId)
  34. .addParams("events", events)
  35. .addParams("params", params)
  36. .build()
  37. when (level) {
  38. ERROR -> logger.error(message)
  39. WARN -> logger.warn(message)
  40. else -> logger.info(message)
  41. }
  42. }
  43. }
  44. /**
  45. * The implementation of this class can be further extended.
  46. *
  47. * Here is a simple reference implementation that can be used as it is.
  48. */
  49. open class LogMessage {
  50. private val builder: StringBuilder
  51. constructor(breadcrumbId: String){
  52. builder = StringBuilder("[$breadcrumbId]")
  53. }
  54. fun addParams(name: Any, value: Any?): LogMessage {
  55. if (value != null) {
  56. builder.append(", $name: $value")
  57. }
  58. return this
  59. }
  60. fun build(): String {
  61. return builder.toString()
  62. }
  63. }


我以前在anyString上也遇到过类似的问题,但是通过anyString的函数扩展解决了这个问题,但是对于anyObject,我无法提出类似的实现。
当前的测试是否有任何方法可以在没有NullPointerException的情况下使用函数或其他替代方案?

oxf4rvwz

oxf4rvwz1#

这是一个有趣的问题。因为Kotlin不能接受传递给一个不能接收null的方法的null,所以它会发疯。我做了一些阅读和实验。似乎唯一的方法就是这样做。

  1. object Helper {
  2. fun <T> anyObject(item: Class<T>, result: T): T {
  3. EasyMock.anyObject(item)
  4. return result
  5. }
  6. fun anyString(): String {
  7. EasyMock.anyString()
  8. return ""
  9. }
  10. }

字符串

  1. @Test
  2. fun `should test logger implementation`() {
  3. val result : Logger = mock(Logger::class.java)
  4. loggerService.info(Helper.anyObject(Logger::class.java, result), Helper.anyString())
  5. replay(loggerService)
  6. myService.`generate different logs based on incoming numbers`(0)
  7. verify(loggerService)
  8. }


在本例中,anyStringanyObject不再返回null

  1. fun <T> anyObject(item: Class<T>): T {
  2. EasyMock.anyObject(item)
  3. return mock(item)
  4. }


因为你不能在匹配器中创建mock。这让EasyMock抓狂。
可悲的是,当使用EasyMock 5.2.0和Java 21执行此操作时,mock下的拦截器似乎没有正常工作。我们得到了一个java.lang.IllegalStateException: matcher calls were used outside expectations。我不知道为什么,但很可能是因为Kotlin为LoggerService生成了一个奇怪的类。如果你有相同的结果,你可以提交一个EasyMock bug。

展开查看全部

相关问题