Mockito:模拟私有字段初始化

Aru*_*run 65 java junit4 mockito powermockito

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

例如

class Test {
    private Person person = new Person();
    ...
    public void testMethod() {
        person.someMethod();
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)

在这里,我想在测试方法时测试person.someMethod() - Test#testMethod.

我需要模拟person变量的初始化.任何线索?

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

Ral*_*alf 86

Mockito附带一个助手类来为您节省一些反射锅炉板代码:

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

//...

@Mock
private Person mockedPerson;
private Test underTest;

// ...

@Test
public void testMethod() {
    Whitebox.setInternalState(underTest, "person", mockedPerson);
    // ...
}
Run Code Online (Sandbox Code Playgroud)

更新: 不幸的是,球队的Mockito决定删除类中2的Mockito所以你回过头来编写你自己的倒影样板代码,使用另一个库(如Apache的共享郎),或者干脆偷了白盒类(它是MIT许可).

更新2: JUnit 5附带了自己的ReflectionSupportAnnotationSupport类,这些类可能很有用,可以避免您拉入另一个库.


Raj*_*mar 39

聚会很晚,但我被这里的朋友打扰了.事情不是使用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();

}
Run Code Online (Sandbox Code Playgroud)

这样,您可以传递模拟对象,然后再进行验证.

参考:

https://www.codota.com/code/java/methods/org.mockito.internal.util.reflection.FieldSetter/set

  • `FieldSetter` 在 Mockito 2.x 中不再可用。 (4认同)
  • @Ralf我正在使用版本嘲笑核心-2.15.0。它在那里。https://static.javadoc.io/org.mockito/mockito-core/2.0.15-beta/org/mockito/internal/util/reflection/FieldSetter.html。虽然仍然是beta。 (3认同)
  • 使用内部 API 并不是最好的主意 (2认同)
  • 我不明白,这怎么能用在私人领域呢?我认为 underTest.getClass().getDeclaredField("person") 会抛出 NoSuchFieldException 。 (2认同)

Aru*_*run 25

我已经找到了这个问题的解决方案,我忘了在这里发布.

@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();
    }
}
Run Code Online (Sandbox Code Playgroud)

该解决方案的关键点是:

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

  • 你的解释讨论了`mockStatic`函数,但是你的代码示例中没有表示.代码示例应该具有`mockStatic`调用,还是构造函数不需要? (5认同)

Zim*_*ger 18

如果您使用Spring Test,请尝试org.springframework.test.util.ReflectionTestUtils

 ReflectionTestUtils.setField(testObject, "person", mockedPerson);
Run Code Online (Sandbox Code Playgroud)

  • @SolankiVaibhav 为什么不呢? (3认同)
  • 伟大的!可能有助于链接到本文,并可能包含其中需要的依赖项:https://www.baeldung.com/spring-reflection-test-utils (2认同)

Sey*_*han 11

如果你正在使用 spring boot 测试并且找不到WhiteBox, FeildSetter;你可以简单地使用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);
    // ...
}
Run Code Online (Sandbox Code Playgroud)


小智 9

到目前为止最好的方法,我认为是

org.springframework.test.util.ReflectionTestUtils

我即将模拟private String mockFieldFooServiceTest.java 中的 FooService.class

FooService.java:

  @Value("${url.image.latest}")
  private String latestImageUrl;
Run Code Online (Sandbox Code Playgroud)

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
  }
Run Code Online (Sandbox Code Playgroud)


Jar*_*ček 8

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

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

new FieldSetter(client, Client.class.getDeclaredField("mapper")).set(new Mapper());
Run Code Online (Sandbox Code Playgroud)


小智 5

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

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

但请注意,应谨慎处理将私有值设置为不同的情况。如果它们是私人的,则是出于某种原因。

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