如何在测试中注入私有List字段以测试JdbcItemDao的方法?
嘿,我来帮你解决这个测试难题!你现在遇到的问题核心在于:你mock了JdbcItemDao类,但mock对象默认不会执行真实的checkStockItems逻辑,而且私有字段tempStockList没法直接注入测试数据,导致断言失败。下面给你几个实用的解决方案,从最佳实践到临时应急方案都有:
解决方案1:重构JdbcItemDao,依赖注入测试数据(推荐)
这是最符合软件测试规范的做法,我们把tempStockList的初始化逻辑从构造方法中解耦,通过构造方法注入列表,这样测试时可以直接传入我们自己准备的测试数据,完全不需要依赖数据库。
修改后的JdbcItemDao代码:
public class JdbcItemDao { private List<Item> tempStockList; // 生产环境使用的构造方法,调用原有的数据库查询逻辑 public JdbcItemDao() { this.tempStockList = getAllStockItems(); } // 测试专用构造方法,直接传入自定义的List public JdbcItemDao(List<Item> tempStockList) { this.tempStockList = tempStockList; } public boolean checkStockItems(String itemName) { for (Item item : tempStockList) { if (item.getItemName().equalsIgnoreCase(itemName)) { return true; } } return false; } // 原有的数据库查询方法(保持不变) private List<Item> getAllStockItems() { // 从MySQL获取数据的逻辑 return new ArrayList<>(); } }
对应的测试类可以直接使用真实的JdbcItemDao实例,传入测试数据:
public class JdbcTest { JdbcItemDao jdbcItemDao; List<Item> tempStockList; @Before public void setup() { tempStockList = new ArrayList<>(); tempStockList.add(new Item(1, "LEDTv", new BigInteger("40"), new Integer("3"), new BigInteger("70"))); // 使用测试构造方法创建真实的Dao对象 jdbcItemDao = new JdbcItemDao(tempStockList); } @Test public void checkStockItemsName() throws Exception { // 测试存在的商品 assertTrue(jdbcItemDao.checkStockItems("LEDTv")); // 额外测试不存在的商品,让测试更全面 assertFalse(jdbcItemDao.checkStockItems("SmartPhone")); } }
解决方案2:用反射设置私有字段(无需重构原类)
如果因为项目限制不能修改JdbcItemDao的代码,那可以用Java反射机制强制访问并设置私有字段tempStockList,这样我们可以创建真实的Dao对象,然后替换它的私有列表为测试数据。
修改后的测试类代码:
import java.lang.reflect.Field; public class JdbcTest { JdbcItemDao jdbcItemDao; List<Item> tempStockList; @Before public void setup() throws NoSuchFieldException, IllegalAccessException { jdbcItemDao = new JdbcItemDao(); tempStockList = new ArrayList<>(); tempStockList.add(new Item(1, "LEDTv", new BigInteger("40"), new Integer("3"), new BigInteger("70"))); // 通过反射获取私有字段 Field field = JdbcItemDao.class.getDeclaredField("tempStockList"); field.setAccessible(true); // 开启私有字段的访问权限 field.set(jdbcItemDao, tempStockList); // 将测试列表注入到Dao对象中 } @Test public void checkStockItemsName() throws Exception { assertTrue(jdbcItemDao.checkStockItems("LEDTv")); } }
⚠️ 注意:反射会破坏类的封装性,尽量只在迫不得已的场景下使用,长期来看还是重构代码更利于维护。
解决方案3:用Mockito Spy实现部分Mock
如果你想继续使用Mockito,可以用spy代替mock。Spy对象会默认执行真实的方法逻辑,同时我们可以mock掉getAllStockItems方法,让它返回我们准备的测试列表(因为原构造方法中调用了这个私有方法)。
修改后的测试类代码:
import org.mockito.Mockito; import static org.mockito.Mockito.doReturn; public class JdbcTest { JdbcItemDao jdbcItemDao; List<Item> tempStockList; @Before public void setup() { tempStockList = new ArrayList<>(); tempStockList.add(new Item(1, "LEDTv", new BigInteger("40"), new Integer("3"), new BigInteger("70"))); // 创建基于真实对象的Spy实例 jdbcItemDao = Mockito.spy(new JdbcItemDao()); // Mock私有方法getAllStockItems,返回测试列表 doReturn(tempStockList).when(jdbcItemDao, "getAllStockItems"); } @Test public void checkStockItemsName() throws Exception { assertTrue(jdbcItemDao.checkStockItems("LEDTv")); } }
注意:mock私有方法需要确保你的Mockito版本支持该功能(Mockito 3及以上版本通常都支持)。
内容的提问来源于stack exchange,提问作者pinq
相关产品推荐
相关产品推荐

