Generics Mockito:泛型父类上的ClassCastException

Generics Mockito:泛型父类上的ClassCastException,generics,kotlin,mockito,Generics,Kotlin,Mockito,为了简化单元测试,我创建了以下父类: internal abstract class TestUnitController<CONTROLLER: Any, SERVICE: Any> { @Mock protected lateinit var service: SERVICE @InjectMocks protected lateinit var controller: CONTROLLER protected abstract fu

为了简化单元测试,我创建了以下父类:

internal abstract class TestUnitController<CONTROLLER: Any, SERVICE: Any> {

    @Mock
    protected lateinit var service: SERVICE

    @InjectMocks
    protected lateinit var controller: CONTROLLER

    protected abstract fun controllerCall(): CONTROLLER.() -> Unit
    protected abstract fun serviceCall(): SERVICE.() -> Any?

    @Test
    fun `when service returns error should throw ResponseStatusException`() {
        testControllerServiceError(service, serviceCall()) { controllerCall() }
    }

    protected fun <U> testController(value: U, assertions: () -> Unit) {
        testController(service, serviceCall(), value) { assertions() }
    }
}

您遇到了Java的泛型类型擦除问题

当Mockito创建并注入mock时,
service
实际上看起来像一个简单的
对象
Any

protected lateinit var service: Any
因此,mock本身被创建为
对象的mock

相反,我建议您在实际的测试类中包含与Mockito相关的所有内容

internal abstract class TestUnitController<CONTROLLER: Any, SERVICE: Any> {
    protected abstract val service: SERVICE

    protected abstract fun controllerCall(): CONTROLLER.() -> Unit
    protected abstract fun serviceCall(): SERVICE.() -> Any?

    @Test
    fun `when service returns error should throw ResponseStatusException`() {
        testControllerServiceError(service, serviceCall()) { controllerCall() }
    }

    protected fun <U> testController(value: U, assertions: () -> Unit) {
        testController(service, serviceCall(), value) { assertions() }
    }
}
内部抽象类TestUnitController{
受保护的抽象val服务:服务
受保护的抽象趣味控制器调用():控制器。(->单元
受保护的抽象趣味serviceCall():服务。(->有吗?
@试验
fun`when service returns error应该抛出ResponseStatusException`(){
testControllerServiceError(服务,serviceCall()){controllerCall()}
}
受保护的fun testController(值:U,断言:()->Unit){
testController(服务,serviceCall(),值){assertions()}
}
}
@RunWith(MockitoJUnitRunner::class)
内部类TestUnitCreateCompanyController:TestUnitController(){
@嘲弄
覆盖lateinit var服务:CompanyService
@注射模拟
私有lateinit var控制器:CreateCompanyController
私有val请求=公司创建请求(“LOL SARL”)
重写fun controllerCall():CreateCompanyController。(->Unit={createCompany(请求)}
重写fun serviceCall():CompanyService.(->Any?={createCompany(Any())}
@试验
fun`create company service成功时应返回created company`(){
val域=公司域(名称=请求.名称)
testController(域){
controller.createCompany(请求)。运行{
uuid.shouldEqual(domain.uuid.toString())
name.shouldEqual(域名)
}
}
}
}

完全正确!谢谢!只是一个小小的更正:
TestUnitCreateCompanyController
service
controller
应该是特定类型,而不是泛型。
protected lateinit var service: Any
internal abstract class TestUnitController<CONTROLLER: Any, SERVICE: Any> {
    protected abstract val service: SERVICE

    protected abstract fun controllerCall(): CONTROLLER.() -> Unit
    protected abstract fun serviceCall(): SERVICE.() -> Any?

    @Test
    fun `when service returns error should throw ResponseStatusException`() {
        testControllerServiceError(service, serviceCall()) { controllerCall() }
    }

    protected fun <U> testController(value: U, assertions: () -> Unit) {
        testController(service, serviceCall(), value) { assertions() }
    }
}
@RunWith(MockitoJUnitRunner::class)
internal class TestUnitCreateCompanyController : TestUnitController<CreateCompanyController, CompanyService>() {
    @Mock
    override lateinit var service: CompanyService

    @InjectMocks
    private lateinit var controller: CreateCompanyController

    private val request = CompanyCreateRequest("LOL SARL")

    override fun controllerCall(): CreateCompanyController.() -> Unit = { createCompany(request) }

    override fun serviceCall(): CompanyService.() -> Any? = { createCompany(any()) }

    @Test
    fun `when create company service successful should return created company`() {
        val domain = CompanyDomain(name = request.name)
        testController(domain) {
            controller.createCompany(request).run {
                uuid.shouldEqual(domain.uuid.toString())
                name.shouldEqual(domain.name)
            }
        }
    }
}