如何在JUnit结合Mockito中访问被测类的私有字段
如何在JUnit+Mockito测试中访问被测类的私有List字段?
你现在的场景很典型:被测类里藏着个私有private List<Person> people字段,执行完方法后需要确认特定状态的Person对象是不是已经被放进这个列表里了。先说说你示例里的问题——你Mock了这个List,这其实会让你没法验证真实的对象状态,因为Mock的List只会记录调用行为,不会真的存储添加的对象。下面给你几个实用的解决方案:
方案1:用@Spy注入真实List(推荐)
把Mock换成真实的List,用@Spy注解让Mockito把它注入到被测类的私有字段里,这样被测类对List的所有操作都会作用在这个真实的列表上,测试时直接访问它就行:
@RunWith(MockitoJUnitRunner.class) public class Testclass { @InjectMocks private TestedClass tested; // 用真实ArrayList,@Spy会把它注入被测类的people字段(名字不匹配就加name属性) @Spy(name = "people") private List<Person> people = new ArrayList<>(); @Test public void testPersonAddedCorrectly() { // 执行被测方法 tested.addPersonWithSpecificState(); // 直接验证列表内容 assertEquals(1, people.size()); Person addedPerson = people.get(0); // 验证对象状态,比如是不是激活状态、名字是否正确 assertTrue(addedPerson.isActive()); assertEquals("Flamp", addedPerson.getLastName()); } }
这个方法既保留了封装性,又能方便验证真实的对象状态,是最推荐的方式。
方案2:用反射直接读取私有字段
如果不想用Spy,反射也是个直接的办法,虽然代码有点繁琐,但胜在不需要修改被测类或依赖Spy:
@Test public void testPersonAddedWithReflection() throws NoSuchFieldException, IllegalAccessException { // 执行被测方法 tested.addPersonWithSpecificState(); // 反射获取私有字段,设置为可访问 Field peopleField = TestedClass.class.getDeclaredField("people"); peopleField.setAccessible(true); List<Person> people = (List<Person>) peopleField.get(tested); // 验证逻辑 assertNotNull(people); assertEquals(1, people.size()); Person targetPerson = people.get(0); assertEquals("Jack", targetPerson.getFirstName()); }
注意:如果被测类的字段名改了,这个测试会直接报错,维护性不如方案1,所以尽量少用。
方案3:给被测类加测试专用的访问方法(最符合封装)
如果可以修改被测类,优先考虑加一个包访问权限的getter方法(别随便改成public,破坏封装),这样测试时就能直接调用:
// 被测类里的方法(和测试类同包的话,不用加public) List<Person> getPeople() { return people; } // 测试类里的验证逻辑 @Test public void testPersonAddedWithGetter() { tested.addPersonWithSpecificState(); List<Person> people = tested.getPeople(); // 验证对象状态... }
这个方法是最符合面向对象封装原则的,毕竟测试也应该尽量通过类的公开/包内接口来验证状态。
最后再提醒一句:你之前Mock List的思路不对,因为Mock的List不会真的存储对象,只能验证有没有调用add方法,但没法验证添加的对象是不是你期望的那个特定状态的实例,所以一定要换成真实的List来测试哦。
内容的提问来源于stack exchange,提问作者Jack Flamp
相关产品推荐
相关产品推荐

