Mockito: 模拟私有字段初始化

如何模拟正在内联初始化的字段变量?

class Test {
private Person person = new Person();
...
public void testMethod() {
person.someMethod();
...
}
}

这里我想模拟 person.someMethod(),同时测试我需要模拟 person变量初始化的 Test.testMethod()方法。有线索吗?

编辑: 我不允许修改 Person 类。

291596 次浏览

Mockito 附带了一个 helper 类,可以为您节省一些反射锅炉板代码:

import org.mockito.internal.util.reflection.Whitebox;


//...


@Mock
private Person mockedPerson;
private Test underTest;


// ...


@Test
public void testMethod() {
Whitebox.setInternalState(underTest, "person", mockedPerson);
// ...
}

更新: 不幸的是,Mockito 团队决定在 Mockito 2中使用 取消课程。因此,您只能重新编写自己的反射样板代码,使用另一个库(例如 Apache Commons Lang) ,或者干脆窃取 白盒类(它是 麻省理工的执照)。

更新2: JUnit5提供了自己的 反射支持注释支持类,这些类可能会很有用,并且可以避免使用另一个库。

我已经找到了解决这个问题的办法,我忘记在这里张贴。

@RunWith(PowerMockRunner.class)
@PrepareForTest({ Test.class })
public class SampleTest {


@Mock
Person person;


@Test
public void testPrintName() throws Exception {
PowerMockito.whenNew(Person.class).withNoArguments().thenReturn(person);
Test test= new Test();
test.testMethod();
}
}

这一解决方案的关键点是:

  1. 使用 PowerMockRunner: @RunWith(PowerMockRunner.class)运行我的测试用例

  2. 指示 Powermock 为操作私有字段准备 Test.class: @PrepareForTest({ Test.class })

  3. 最后模拟 Person 类的构造函数:

    PowerMockito.mockStatic(Person.class); PowerMockito.whenNew(Person.class).withNoArguments().thenReturn(person);

派对已经很晚了,但我在这里被袭击了,得到了一个朋友的帮助。问题不在于使用 PowerMock。这适用于最新版本的 Mockito。

Mockito 带了这个 org.mockito.internal.util.reflection.FieldSetter

它的基本功能是帮助您使用反射修改私有字段。

这就是你如何使用它:

@Mock
private Person mockedPerson;
private Test underTest;


// ...


@Test
public void testMethod() {
FieldSetter.setField(underTest, underTest.getClass().getDeclaredField("person"), mockedPerson);
// ...
verify(mockedPerson).someMethod();
}

通过这种方式,您可以传递一个模拟对象,然后在以后对其进行验证。

这里 是引用。

下面的代码可用于在 REST 客户端模拟中初始化映射器。mapper字段是私有的,需要在单元测试设置期间设置。

import org.mockito.internal.util.reflection.FieldSetter;


new FieldSetter(client, Client.class.getDeclaredField("mapper")).set(new Mapper());

如果需要为所有测试设置相同的变量值,可以使用@Jarda 的指南来定义:

@Before
public void setClientMapper() throws NoSuchFieldException, SecurityException{
FieldSetter.setField(client, client.getClass().getDeclaredField("mapper"), new Mapper());
}

但是要注意,将私有值设置为不同的值时,应该小心处理。如果他们是私人的,是出于某种原因。

例如,我使用它来更改单元测试中睡眠的等待时间。在实际的例子中,我想睡10秒钟,但在单元测试中,如果它是立即的,我会感到满意。在集成测试中,您应该测试真正的值。

如果您使用 Spring 测试,请尝试 Test.util.RefectionTestUtils

 ReflectionTestUtils.setField(testObject, "person", mockedPerson);

如果您正在使用弹簧启动测试,并且找不到 WhiteBoxFeildSetter; 您可以简单地使用 org.springframework.test.util.ReflectionTestUtils

这是一个例子:

import org.springframework.test.util.ReflectionTestUtils;


//...


@Mock
private Person mockedPerson;
private Test underTest;


// ...


@Test
public void testMethod() {
ReflectionTestUtils.setField(underTestObject, "person", mockedPerson);
// ...
}

直到现在,我认为最好的办法是

org.springframework.test.util.ReflectionTestUtils

我要在 FooServiceTest.java 中的 FooService.class 中模拟 private String mockField

FooService.java:

  @Value("${url.image.latest}")
private String latestImageUrl;

Java:

  @InjectMocks
FooService service;


@BeforeEach
void setUp() {
ReflectionTestUtils.setField(service, // inject into this object
"latestImageUrl", // assign to this field
"your value here"); // object to be injected
}