You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何测试Kotlin中使用lateinit Id的服务函数?

问题分析与解决:UninitializedPropertyAccessException测试错误

错误原因

你遇到的kotlin.UninitializedPropertyAccessException是因为测试中构建的Segmentation对象的id属性是lateinit类型,未被初始化,但在createSegmentFromUserIds函数中,会调用querySegmentService.runUpdateQuery(..., segmentId = segmentation.id),尝试访问这个未初始化的属性,从而抛出异常。

另外,你的测试中同时mock了segmentationRepository和segmentMetaService,但实际上segmentMetaService.saveSegmentInfo的mock已经覆盖了内部的仓库调用,这部分mock属于冗余代码。

修复步骤

1. 初始化Segmentation的id属性

创建测试用的Segmentation实例时,直接为id赋值:

val segment = Segmentation(
    attribute = filter, 
    name = "segment-name", 
    type = SegmentType.STATIC, 
    appId = "app-id" 
).apply {
    id = "test-segment-id" // 手动初始化lateinit属性
}

2. 移除冗余的mock代码

由于你已经mock了segmentMetaService.saveSegmentInfo的返回值,不需要再mocksegmentationRepository.save,直接删除这行代码:

// 删掉这行:`when`(segmentationRepository.save(any())).thenReturn(segment)

3. 验证依赖方法的调用(推荐)

为了确保测试的完整性,添加对querySegmentService.runUpdateQuery的调用验证:

// 在assertEquals之后添加
verify(querySegmentService).runUpdateQuery(userIds = userIds, appId = "app-id", segmentId = "test-segment-id")

修改后的完整测试代码:

class SegmentServiceTest {
    private val segmentMetaService = mock(SegmentMetaService::class.java)
    private val querySegmentService = mock(QuerySegmentService::class.java)
    private val converterService = mock(ConverterService::class.java)
    private val userAttributeService = mock(UserAttributeService::class.java)
    private val segmentConsumerUserInfoProducer = mock(SegmentConsumerUsersInfoProducer::class.java)

    @Test
    fun `createSegmentFromUserIds should create a new segment`() {
        val segmentService = SegmentService(
            segmentMetaService = segmentMetaService,
            querySegmentService = querySegmentService,
            converterService = converterService,
            userAttributeService = userAttributeService,
            segmentConsumerUserInfoProducer = segmentConsumerUserInfoProducer
        )

        val userIds = listOf("user-1", "user-2", "user-3")
        val filter = AndFilter(
            operations = listOf(
                AndFilter(
                    operations = listOf(
                        StringListContainsFilter(
                            field = "userId", operationType = StringQueryOperationType.IN, values = userIds
                        )
                    )
                )
            )
        )
        val createSegmentRequest = CreateSegmentRequest(
            name = "segment-name", attribute = filter, type = SegmentType.STATIC
        )
        val segment = Segmentation(
            attribute = filter, 
            name = "segment-name", 
            type = SegmentType.STATIC, 
            appId = "app-id" 
        ).apply {
            id = "test-segment-id"
        }

        `when`(segmentMetaService.saveSegmentInfo(createSegmentRequest, "app-id")).thenReturn(segment)
        val createdSegment = segmentService.createSegmentFromUserIds(userIds = userIds, name = "segment-name", appId = "app-id")
        
        assertEquals(segment, createdSegment)
        verify(querySegmentService).runUpdateQuery(userIds, "app-id", "test-segment-id")
    }
}

可测试性优化建议

1. 修改实体类的id属性类型

避免使用lateinit存储数据库主键,改为可空类型,这样即使未初始化也不会抛出异常,Spring Data MongoDB在保存时会自动填充该值:

@Document(Segmentation.COLLECTION_NAME)
class Segmentation(
    // ... 其他属性
) {
    @Id
    var id: String? = null // 替换lateinit为可空类型
    // ... 其他属性
}

2. 简化测试依赖

使用Mockito的注解(如@Mock、@InjectMocks)来简化测试类的依赖初始化,减少手动创建mock的代码:

class SegmentServiceTest {
    @Mock
    private lateinit var segmentMetaService: SegmentMetaService
    @Mock
    private lateinit var querySegmentService: QuerySegmentService
    @Mock
    private lateinit var converterService: ConverterService
    @Mock
    private lateinit var userAttributeService: UserAttributeService
    @Mock
    private lateinit var segmentConsumerUserInfoProducer: SegmentConsumerUsersInfoProducer

    @InjectMocks
    private lateinit var segmentService: SegmentService

    @BeforeEach
    fun setUp() {
        MockitoAnnotations.openMocks(this)
    }

    // ... 测试方法
}

内容的提问来源于stack exchange,提问作者arianhf

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 01:00:54