使用Mockito测试Android数据库DBHelper类时断言失败问题求助
问题根本原因
- 当你使用
mock(DBHelper.class)创建全类mock对象时,Mockito默认不会执行该对象的任何真实方法逻辑,所有成员变量也会被初始化为对应类型的默认值,此处cityDataList就是初始状态的空ArrayList。 - 你仅对
refreshDataList()方法做了打桩指定返回值,但没有对getNumberOfCities()做任何打桩配置,调用该方法时会直接返回内部默认空列表的长度0,和你之前打桩的refreshDataList()返回值没有任何关联。 - 补充说明:原DBHelper的
refreshDataList()本身是异步逻辑,就算不使用mock走真实逻辑,调用方法后直接取列表长度也拿不到正确值,因为SnapshotListener的回调是异步执行的,返回列表时内部数据还未被填充。
可选用的解决方案
根据你的测试目的不同,可以选择以下两种方案:
方案1:测试DBHelper本身的业务逻辑
这种场景不要mock DBHelper本身,改为mock它的外部依赖(FirebaseFirestore相关对象),避免真实调用远程数据库:
- 先改造DBHelper的构造方法,改用依赖注入传入FirebaseFirestore实例,方便测试时注入mock实例:
public class DBHelper { private static final String TAG = "DB Utility"; FirebaseFirestore db; CollectionReference cityCollection; ArrayList<City> cityDataList; // 改用依赖注入传入Firebase实例,不要在构造方法内直接初始化 public DBHelper(FirebaseFirestore db){ this.db = db; this.cityCollection = db.collection("Cities"); this.cityDataList = new ArrayList<>(); } // 剩余原有逻辑不变 }
- 测试时mock FirebaseFirestore、CollectionReference实例,对
addSnapshotListener方法打桩,直接触发回调传入模拟的QuerySnapshot数据,验证cityDataList的填充逻辑是否符合预期。
方案2:仅测试依赖DBHelper的其他类,不需要验证DBHelper本身逻辑
这种场景可以直接对测试用到的所有DBHelper方法进行打桩,或者使用Spy实现部分真实逻辑调用:
方式A:直接对所有调用到的方法打桩
@Test public void testCityList(){ DBHelper dbHelper = mock(DBHelper.class); ArrayList<City> cityList = new ArrayList<>(); cityList.add(new City("Edmonton", "Alberta")); cityList.add(new City("Vancouver", "British Columbia")); // 对两个用到的方法分别打桩 when(dbHelper.refreshDataList()).thenReturn(cityList); when(dbHelper.getNumberOfCities()).thenReturn(2); assertEquals(2, dbHelper.getNumberOfCities()); }
方式B:使用Spy实现部分真实逻辑
Spy是基于真实对象的代理,未打桩的方法会走真实逻辑:
@Test public void testCityList() throws NoSuchFieldException, IllegalAccessException { DBHelper dbHelper = spy(new DBHelper()); ArrayList<City> cityList = new ArrayList<>(); cityList.add(new City("Edmonton", "Alberta")); cityList.add(new City("Vancouver", "British Columbia")); // 打桩refreshDataList返回模拟列表 when(dbHelper.refreshDataList()).thenReturn(cityList); // 通过反射给DBHelper的cityDataList赋值为模拟列表 Field field = DBHelper.class.getDeclaredField("cityDataList"); field.setAccessible(true); field.set(dbHelper, cityList); // getNumberOfCities走真实逻辑,返回列表长度2 assertEquals(2, dbHelper.getNumberOfCities()); }
内容的提问来源于stack exchange,提问作者akalanka
相关产品推荐
相关产品推荐

