save(...) 不得为空

问题描述 投票:0回答:1

我尝试在 Spring boot + Kotlin 上的简单 CRUD 应用程序中进行单元测试。 我已经对其他实体和存储库进行了测试,但用户实体和存储库存在问题。 该表链接到表 Team,可能这就是为什么我的测试的标准逻辑不起作用的原因。

Springboot版本“3.2.4”

实体

@Entity
@Table(name = "\"user\"")
@DynamicUpdate
class UserEntity(

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_generator")
    @SequenceGenerator(name = "user_generator", sequenceName = "user_id_seq", allocationSize = 1)
    @Column(name = "id")
    var userId: Int?,

    @Column(name = "email")
    val email: String,

    @Enumerated(EnumType.STRING)
    @Column(name = "role", columnDefinition = "user_role_enum", nullable = false)
    val role: Role,

    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(
        name = "user_teams_team",
        joinColumns = [JoinColumn(name = "user_id")],
        inverseJoinColumns = [JoinColumn(name = "team_id")]
    )
    val teams: Set<TeamEntity>? = null
)

@Entity
@Table(name = "team")
@DynamicUpdate
class TeamEntity(

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "team_generator")
    @SequenceGenerator(name = "team_generator", sequenceName = "team_id_seq", allocationSize = 1)
    @Column(name = "id")
    var teamId: Int?,

    @Column(name = "name_ru")
    val nameRu: String,

    @Column(name = "name_en")
    val nameEn: String,

    @Column(name = "name_kk")
    val nameKk: String
)

DTO

class UserDto(
    val email: String,
    val role: Role,
    val teams: List<Int>?
)
data class UserResponseDto(
    val userId: Int,
    val email: String,
    val role: Role,
    var teams: Set<TeamEntity>?
)

用户服务实现

override fun createUser(userDto: UserDto): UserResponseDto {
   checkIfSuchTeamsExistOrThrowNotFoundException(userDto)

   val teamSet = teamServiceImpl.createTeamSetFromTeamIdsOrThrowNotFoundException(userDto)
   val savedUser = userRepository.save(userMapper.toEntity(userDto, teamSet))
   return userMapper.toDto(savedUser)

}

private fun checkIfSuchTeamsExistOrThrowNotFoundException(userDto: UserDto) {
        val existingInDbTeamIdList = teamRepository.findAll().map { it.teamId!! }

        userDto.teams?.forEach { teamId ->
            if (teamId !in existingInDbTeamIdList) {
                throw NotFoundException("There is no team with id: $teamId")
            }
        }
    }

团队服务实现

fun createTeamSetFromTeamIdsOrThrowNotFoundException(userDto: UserDto): MutableSet<TeamEntity> {
    val teamSet: MutableSet<TeamEntity> = mutableSetOf()
    userDto.teams?.forEach { teamId ->
        val teamEntity = teamRepository.findById(teamId)
            .orElseThrow { NotFoundException("There is no team with id: $teamId") }
        teamSet.add(teamEntity)
    }
    return teamSet
}

UserServiceImplTests

@ExtendWith(SpringExtension::class)
@TestInstance(TestInstance.Lifecycle.PER_METHOD)
class UserServiceImplTests {

@Mock
private lateinit var teamMapper: TeamMapper

@Mock
private lateinit var userRepository: UserRepository

@Mock
private lateinit var teamRepository: TeamRepository

@Mock
private lateinit var teamService: TeamServiceImpl

@Mock
private lateinit var userMapper: UserMapper

@InjectMocks
private lateinit var userService: UserServiceImpl

@Captor
private lateinit var userEntityCaptor: ArgumentCaptor<UserEntity>

@Test
fun createUser_Success() {
    val id = 1
    val teamEntity = TeamEntity(id, "testRu", "testEn", "testKk")
    val userEntity = UserEntity(id, "test", Role.OWNER, setOf(teamEntity))
    val userEntity1 = UserEntity(id, "test", Role.OWNER, setOf(teamEntity))
    val teamSet = mutableSetOf(teamEntity)
    val userDto = UserDto("test", Role.OWNER, listOf(1))
    val userResponseDto = UserResponseDto(id, "test", Role.OWNER, setOf(teamEntity))

    whenever(teamRepository.findAll()).thenReturn(listOf(teamEntity))
    whenever(teamRepository.findById(id)).thenReturn(Optional.of(teamEntity))
    whenever(userMapper.toEntity(userDto, teamSet)).thenReturn(userEntity)
    whenever(userRepository.save(userEntity)).thenReturn(userEntity)
    whenever(userMapper.toDto(userEntity)).thenReturn(userResponseDto)

    val result = userService.createUser(userDto)
    assertEquals(userResponseDto, result)

    verify(userRepository).save(userEntityCaptor.capture())
    val capturedEntity = userEntityCaptor.value
    assertNotNull(capturedEntity)
    assertEquals(userEntity, capturedEntity)

    verify(userRepository).save(userEntity)
    verify(teamRepository).findAll()
}

这就是错误本身:

save(...) must not be null
java.lang.NullPointerException: save(...) must not be null
    at kt.cell.jobsapi.service.impl.UserServiceImpl.createUser(UserServiceImpl.kt:39)
    at kt.cell.jobsapi.service.impl.UserServiceImplTests.createUser_Success(UserServiceImplTests.kt:123)

我只是不明白去哪里寻找

spring-boot unit-testing mockito
1个回答
0
投票

您在测试代码周围使用mockito-kotlin,但对ArgumentCaptor使用普通mockito。

ArgumentCaptor.capture返回其泛型参数UserEntity的默认值,即null。

要解决此问题,请使用mockito-kotlin 中的捕获,它会为对象创建通用参数的新实例,以防止 NPE。

参见相关定义ArgumentCaptor.kt

inline fun <reified T : Any> capture(captor: ArgumentCaptor<T>): T {
    return captor.capture() ?: createInstance()
}
© www.soinside.com 2019 - 2024. All rights reserved.