Mockito:模拟私有字段初始化

Mockito: Mock private field initialization

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

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

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

编辑:我不允许修改人物class。

Mockito 附带了一个帮助程序 class 来为您节省一些反射样板代码:

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

//...

@Mock
private Person mockedPerson;
private Test underTest;

// ...

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

更新: 不幸的是,mockito 团队决定 remove the class in Mockito 2. So you are back to writing your own reflection boilerplate code, use another library (e.g. Apache Commons Lang), or simply pilfer the Whitebox class (it is MIT licensed).

更新二: JUnit 5 附带了自己的 ReflectionSupport and AnnotationSupport classes,它们可能很有用,可以让您免于引入另一个库。

我已经找到了这个问题的解决方案,但我忘记了 post 在这里。

@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 的构造函数 class:

    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();
}

这样你就可以传递一个模拟对象,然后再验证它。

Here为参考。

以下代码可用于在 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测试试试org.springframework.test.util.ReflectionTestUtils

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

如果您正在使用 spring 启动测试并且找不到 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;

FooServiceTest.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
  }