You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何用Mockito模拟创建新对象的方法?测试场景疑难求解

解决temp类updateDetails()方法中无法模拟storage实例的问题

你遇到的核心问题是代码紧耦合updateDetails()方法里直接实例化了storage()对象,导致测试时无法替换成模拟实例,Mockito自然没法拦截这种硬编码的对象创建,最终实际的storage实例因为context未初始化报错。下面给你几个可行的解决方案:

方案1:依赖注入(最推荐)

storage作为依赖通过构造函数传入temp类,这样测试时就能轻松替换成模拟对象。

修改后的temp类:

class temp(private val storage: Storage) { 
    fun updateDetails() { 
        //do some work 
        storage.set(storedata) 
    } 
}

测试代码示例:

import org.junit.Test
import org.mockito.Mockito.*

class TempTest {
    @Test
    fun updateDetails_callsStorageSet() {
        // 模拟Storage实例
        val mockStorage = mock(Storage::class.java)
        val tempInstance = temp(mockStorage)
        
        tempInstance.updateDetails()
        
        // 验证set方法是否被调用
        verify(mockStorage).set(any())
    }
}

这种方式彻底解耦了tempstorage,符合依赖倒置原则,后续维护和测试都更方便。

方案2:用Mockito的mockConstruction API(适合无法修改构造函数的场景)

如果没法改动temp类的构造逻辑,Mockito 3.5及以上版本提供了mockConstruction API,可以拦截类的实例化过程,返回模拟对象。

测试代码示例:

import org.junit.Test
import org.mockito.Mockito.*
import org.mockito.junit.MockitoJUnitRunner
import org.junit.runner.RunWith

@RunWith(MockitoJUnitRunner::class)
class TempTest {
    @Test
    fun updateDetails_callsStorageSet() {
        // 拦截Storage的实例化,返回模拟对象
        mockConstruction(Storage::class.java) { mock, context ->
            // 这里可以对mock做初始化,比如设置所需的context
            `when`(mock.context).thenReturn(mockContext)
        }.use {
            val tempInstance = temp()
            tempInstance.updateDetails()
            
            // 验证模拟的Storage的set方法被调用
            val mockStorage = it.constructed().first()
            verify(mockStorage).set(any())
        }
    }
}

这个方法不需要修改原有业务代码,但依赖Mockito的新版本,而且如果storage有复杂的初始化逻辑,可能需要在lambda里额外处理。

方案3:提取工厂方法(最小改动现有代码)

temp类里提取一个创建storage的工厂方法,测试时通过子类重写这个方法返回模拟对象。

修改后的temp类:

open class temp { 
    fun updateDetails() { 
        //do some work 
        createStorage().set(storedata) 
    }

    // 提取工厂方法,protected/open方便测试时重写
    protected open fun createStorage(): Storage {
        return storage()
    }
}

测试代码示例:

import org.junit.Test
import org.mockito.Mockito.*

class TempTest {
    @Test
    fun updateDetails_callsStorageSet() {
        // 创建temp的子类,重写createStorage返回模拟对象
        val mockStorage = mock(Storage::class.java)
        val tempInstance = object : temp() {
            override fun createStorage(): Storage {
                return mockStorage
            }
        }
        
        tempInstance.updateDetails()
        
        verify(mockStorage).set(any())
    }
}

这种方式对现有代码的改动最小,只需要新增一个工厂方法,适合不能大规模重构的场景。

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

火山引擎 最新活动