MockK单元测试对比LiveData模拟对象时assertEquals失败如何解决
问题原因
报错是对象初始化顺序和MockK打桩时机不匹配导致的:
Repository的response是类初始化阶段就直接赋值的属性,实例化Repository时就会立刻调用dao.getAllObjects()获取值- 你的测试用了
@InjectMockKs注解,MockKAnnotations.init(this)执行时就会直接实例化Repository并注入依赖,这时候你还没有执行every { dao.getAllObjects() } returns mockLiveData打桩逻辑 - 因为你开了
relaxed = true,MockK会为未打桩的dao.getAllObjects()自动生成一个默认的临时LiveData对象赋值给repository.response,后续你在测试方法里的打桩已经不会影响已经完成初始化的response属性了,所以两个对象不一致。
小提示:如果没有特殊需求,单元测试尽量不要开
relaxed = true,未打桩的方法调用直接抛出异常能更快帮你定位初始化顺序、漏打桩这类问题。
修复方案
方案1:不修改生产代码,调整测试逻辑
放弃@InjectMockKs自动注入,在打桩完成后手动创建Repository实例即可,修改后的测试代码如下:
class RepositoryTest { @MockK private lateinit var mockLiveData: LiveData<List<MyObject>> @MockK private lateinit var mockDao: Dao // 移除@InjectMockKs注解,手动控制实例化时机 private lateinit var repository: Repository init { MockKAnnotations.init(this, relaxed = true) } @Test fun `Verify my objects are loaded from the database`() { // 先完成打桩 every { dao.getAllObjects() } returns mockLiveData // 打桩完成后再手动实例化Repository repository = Repository(mockDao) // 执行断言 verify { dao.getAllObjects() } assertEquals(mockLiveData, repository.response) } }
方案2:修改生产代码属性为懒加载,无需调整现有测试逻辑
把Repository的response改为懒加载属性,只有第一次访问该属性时才会执行dao.getAllObjects(),这样打桩时机就早于属性赋值时机:
class Repository @Inject constructor(private val dao: Dao) { // 增加by lazy关键字改为懒加载 val response: LiveData<List<MyObject>> by lazy { dao.getAllObjects() } ... }
该方案下你原有测试代码不需要任何修改,直接运行即可通过。
内容的提问来源于stack exchange,提问作者dudi
相关产品推荐
相关产品推荐

