PowerMockito模拟在调用真实方法后被清除

huangapple go评论83阅读模式
英文:

PowerMockito mock gets cleared after calling real method

问题

我在使用PowerMockito对静态方法进行模拟时遇到了奇怪的行为。

注意: 2020-08-27 是执行测试时的实际系统时间。

在上面的代码示例中,您可以看到我对 LocalDate.now() 方法的所有实现以及 LocalDateTime.now() 方法的所有实现进行了模拟。我同时希望能够获得 LocalDateTime.of(LocalDate, LocalTime) 方法的真实实现。

所有的模拟在调用 LocalDateTime.of(LocalDate, LocalTime) 方法之前都能正常工作。

当我在调用了 LocalDateTime.of(LocalDate, LocalTime) 方法之后调用 LocalDateTime.now() 时,第一次获得的是真实的系统时间,而不是模拟值。但是,如果我再次调用 LocalDateTime.now(),那么我会再次获得模拟时间。

附上了调试截图,显示了模拟结果。这是我使用的代码。

@RunWith(PowerMockRunner.class)
@PowerMockIgnore({"com.sun.org.apache.xerces.*", "javax.xml.*", "org.xml.*", "org.w3c.dom.*"})
// 这是由于Power Mock与JDK 9存在兼容性问题所需的修复(https://github.com/powermock/powermock/issues/864#issuecomment-447001997)
public class MainTest {

    @Test
    public void test() {
        LocalDate now = LocalDate.now();

        String dateString = "2020-08-24T09:00:00Z";
        Instant instant = Instant.parse(dateString);
        LocalDate localDate = LocalDate.ofInstant(instant, ZoneId.of("UTC"));
        LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.of("UTC"));

        PowerMockito.mockStatic(LocalDate.class);
        PowerMockito.when(LocalDate.now()).thenReturn(localDate);
        PowerMockito.when(LocalDate.now(Mockito.any(ZoneId.class))).thenReturn(localDate);
        PowerMockito.when(LocalDate.now(Mockito.any(Clock.class))).thenReturn(localDate);

        PowerMockito.mockStatic(LocalDateTime.class);
        PowerMockito.when(LocalDateTime.of(Mockito.any(LocalDate.class), Mockito.any(LocalTime.class))).thenCallRealMethod();
        PowerMockito.when(LocalDateTime.now()).thenReturn(localDateTime);
        PowerMockito.when(LocalDateTime.now(Mockito.any(ZoneId.class))).thenReturn(localDateTime);
        PowerMockito.when(LocalDateTime.now(Mockito.any(Clock.class))).thenReturn(localDateTime);

        LocalDate now1 = LocalDate.now();
        LocalDateTime of = LocalDateTime.of(localDate, LocalTime.of(10, 30));
        LocalDate now2 = LocalDate.now();
        LocalDate now3 = LocalDate.now();
        System.out.println("");
    }
}

我已经花了几个小时寻找根本原因,但不幸的是还没有找到。有人能告诉我这里出了什么问题吗?

英文:

I'm experiencing a weird behaviour with PowerMockito static method mocking.

PowerMockito模拟在调用真实方法后被清除

Note: 2020-08-27 is the real system time at the time of executing the test.

In the above code example as you can see I have mocked all the implementations of LocalDate.now() method and all the implementations of LocalDateTime.now() method. And I want the real implementation of LocalDateTime.of(LocalDate, LocalTime) method at the same time.

All the mocks are working correctly until I call the LocalDateTime.of(LocalDate, LocalTime) method.

When I call the LocalDateTime.now() after calling LocalDateTime.of(LocalDate, LocalTime) I get the real system time not the mock value for the 1st time. But if I LocalDateTime.now() again then I again get the mocked time.

A debug screenshot is attached above with the mock results. This is the code I'm using.

@RunWith(PowerMockRunner.class)
@PowerMockIgnore({"com.sun.org.apache.xerces.*", "javax.xml.*", "org.xml.*", "org.w3c.dom.*"})
// This is needed due to a compatibility issue of Power Mock with JDK 9 (https://github.com/powermock/powermock/issues/864#issuecomment-447001997)
public class MainTest {
@Test
public void test() {
LocalDate now = LocalDate.now();
String dateString = "2020-08-24T09:00:00Z";
Instant instant = Instant.parse(dateString);
LocalDate localDate = LocalDate.ofInstant(instant, ZoneId.of("UTC"));
LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.of("UTC"));
PowerMockito.mockStatic(LocalDate.class);
PowerMockito.when(LocalDate.now()).thenReturn(localDate);
PowerMockito.when(LocalDate.now(Mockito.any(ZoneId.class))).thenReturn(localDate);
PowerMockito.when(LocalDate.now(Mockito.any(Clock.class))).thenReturn(localDate);
PowerMockito.mockStatic(LocalDateTime.class);
PowerMockito.when(LocalDateTime.of(Mockito.any(LocalDate.class), Mockito.any(LocalTime.class))).thenCallRealMethod();
PowerMockito.when(LocalDateTime.now()).thenReturn(localDateTime);
PowerMockito.when(LocalDateTime.now(Mockito.any(ZoneId.class))).thenReturn(localDateTime);
PowerMockito.when(LocalDateTime.now(Mockito.any(Clock.class))).thenReturn(localDateTime);
LocalDate now1 = LocalDate.now();
LocalDateTime of = LocalDateTime.of(localDate, LocalTime.of(10, 30));
LocalDate now2 = LocalDate.now();
LocalDate now3 = LocalDate.now();
System.out.println("");
}
}

I have spent several hours looking out for the root cause but unfortunately no luck yet. Can someone please tell me what is wrong here?

答案1

得分: 1

这看起来像是 PowerMockito.mockStatic 本身的一个错误。我也在 https://github.com/powermock/powermock/issues/1066 发布了相同的问题,但我还没有收到任何回应。不过无论如何,我已经能够通过 PowerMockito.stub 方法找到了我的用例的解决方案。以下代码片段按预期工作,没有任何问题。

@RunWith(PowerMockRunner.class)
@PowerMockIgnore({"com.sun.org.apache.xerces.*", "javax.xml.*", "org.xml.*", "org.w3c.dom.*"})
// 这是由于 Power Mock 与 JDK 9 的兼容性问题所需 (https://github.com/powermock/powermock/issues/864#issuecomment-447001997)
public class MainTest {

    @Test
    public void test() {
        LocalDate now = LocalDate.now();

        String dateString = "2020-08-24T09:00:00Z";
        Instant instant = Instant.parse(dateString);
        LocalDate localDate = LocalDate.ofInstant(instant, ZoneId.of("UTC"));
        LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.of("UTC"));

        PowerMockito.stub(PowerMockito.method(LocalDate.class, "now")).toReturn(localDate);
        PowerMockito.stub(PowerMockito.method(LocalDate.class, "now", ZoneId.class)).toReturn(localDate);
        PowerMockito.stub(PowerMockito.method(LocalDate.class, "now", Clock.class)).toReturn(localDate);

        PowerMockito.stub(PowerMockito.method(LocalDateTime.class, "now")).toReturn(localDateTime);
        PowerMockito.stub(PowerMockito.method(LocalDateTime.class, "now", ZoneId.class)).toReturn(localDateTime);
        PowerMockito.stub(PowerMockito.method(LocalDateTime.class, "now", Clock.class)).toReturn(localDateTime);

        LocalDate now1 = LocalDate.now();
        LocalDateTime of = LocalDateTime.of(localDate, LocalTime.of(10, 30));
        LocalDate now2 = LocalDate.now();
        LocalDate now3 = LocalDate.now();
        System.out.println("");
    }
}

两种方法之间的区别在于,使用 PowerMockito.mockStatic 我会模拟整个类,然后使用 PowerMockito.when 定义每个方法的行为。那些没有使用 PowerMockito.when 定义的方法,如果被调用,将返回 null 或不执行任何操作。但是使用 PowerMockito.stub,我不会模拟整个类。我仅为一组选定的方法使用 PowerMockito.stub 定义模拟行为,其他方法在被调用时将调用实际方法。

英文:

This looks like a bug with PowerMockito.mockStatic itself. I posted the same at https://github.com/powermock/powermock/issues/1066 also. But I didn't get any response yet. Anyway, I was able to find a solution for my use case with PowerMockito.stub method. The following code piece works as expected without any issue.

@RunWith(PowerMockRunner.class)
@PowerMockIgnore({"com.sun.org.apache.xerces.*", "javax.xml.*", "org.xml.*", "org.w3c.dom.*"})
// This is needed due to a compatibility issue of Power Mock with JDK 9 (https://github.com/powermock/powermock/issues/864#issuecomment-447001997)
public class MainTest {
@Test
public void test() {
LocalDate now = LocalDate.now();
String dateString = "2020-08-24T09:00:00Z";
Instant instant = Instant.parse(dateString);
LocalDate localDate = LocalDate.ofInstant(instant, ZoneId.of("UTC"));
LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.of("UTC"));
PowerMockito.stub(PowerMockito.method(LocalDate.class, "now")).toReturn(localDate);
PowerMockito.stub(PowerMockito.method(LocalDate.class, "now", ZoneId.class)).toReturn(localDate);
PowerMockito.stub(PowerMockito.method(LocalDate.class, "now", Clock.class)).toReturn(localDate);
PowerMockito.stub(PowerMockito.method(LocalDateTime.class, "now")).toReturn(localDateTime);
PowerMockito.stub(PowerMockito.method(LocalDateTime.class, "now", ZoneId.class)).toReturn(localDateTime);
PowerMockito.stub(PowerMockito.method(LocalDateTime.class, "now", Clock.class)).toReturn(localDateTime);
LocalDate now1 = LocalDate.now();
LocalDateTime of = LocalDateTime.of(localDate, LocalTime.of(10, 30));
LocalDate now2 = LocalDate.now();
LocalDate now3 = LocalDate.now();
System.out.println("");
}
}

The difference between 2 approaches are with PowerMockito.mockStatic I mock the whole class and then define the behaviour of each method with PowerMockito.when. The methods which haven't been defined with PowerMockito.when will return null or do nothing if invoked. But with PowerMockito.stub I don't mock the whole class. I define the mocked behaviour only for a selected set of methods with PowerMockito.stub and other methods will call the real methods if invoked.

huangapple
  • 本文由 发表于 2020年8月27日 21:48:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/63617443.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定