【问题标题】:PowerMock Static method stubbing doesn't appear to workPowerMock 静态方法存根似乎不起作用
【发布时间】:2015-02-25 02:29:09
【问题描述】:

我已经在谷歌上搜索并弄乱了我的代码,但最后我似乎无法弄清楚为什么静态模拟不适用于 PowerMock 和 Mockito。

我试图在 JavaHg 库 https://bitbucket.org/aragost/javahg 中模拟 LogCommand 类的 on() 方法,该方法将 javahg BaseRepository 对象作为参数并返回 LogCommand 类的实例。我想让它返回一个模拟日志对象,这样我就可以验证它是否调用了 execute() 命令,但 Mockito 异常不断被抛出。

这是我的代码:

@RunWith(PowerMockRunner.class)
@PrepareForTest(LogCommand.class)
public class MyRepositoryTest {

    private BaseRepository mockHgRepo;
    private MyRepository myRepository;

    @Before
    public void before() {
        mockHgRepo = mock(BaseRepository.class);
        PowerMockito.mockStatic(LogCommand.class);
    }

    @Test
    public void staticMockTest() throws IOException {
        LogCommand mockLogCommand = mock(LogCommand.class);
        when(LogCommand.on(any(BaseRepository.class))).thenReturn(mockLogCommand); // Problem Line!
        myRepository = new MyRepository(mockHgRepo);
    }
}

通过这段代码,我得到了堆栈跟踪:

org.mockito.exceptions.misusing.InvalidUseOfMatchersException: 
Misplaced argument matcher detected here:

-> at com.mycompany.repository.config.MyRepositoryTest.getNextTagNumberTest(MyRepositoryTest.java:39)

You cannot use argument matchers outside of verification or stubbing.
Examples of correct usage of argument matchers:
    when(mock.get(anyInt())).thenReturn(null);
    doThrow(new RuntimeException()).when(mock).someVoidMethod(anyObject());
    verify(mock).someMethod(contains("foo"))

Also, this error might show up because you use argument matchers with methods that cannot be mocked.
Following methods *cannot* be stubbed/verified: final/private/equals()/hashCode().

    at com.mycompany.myRepository.config.MyRepositoryTest.staticMockTest(MyRepositoryTest.java:39)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:68)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:312)
    at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:88)
    at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:96)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.executeTest(PowerMockJUnit44RunnerDelegateImpl.java:296)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit49RunnerDelegateImpl$PowerMockJUnit49MethodRunner.executeTestInSuper(PowerMockJUnit49RunnerDelegateImpl.java:116)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit49RunnerDelegateImpl$PowerMockJUnit49MethodRunner.executeTest(PowerMockJUnit49RunnerDelegateImpl.java:77)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runBeforesThenTestThenAfters(PowerMockJUnit44RunnerDelegateImpl.java:284)
    at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:86)
    at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:49)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.invokeTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:209)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.runMethods(PowerMockJUnit44RunnerDelegateImpl.java:148)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$1.run(PowerMockJUnit44RunnerDelegateImpl.java:122)
    at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:33)
    at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:45)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.run(PowerMockJUnit44RunnerDelegateImpl.java:120)
    at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.run(JUnit4TestSuiteChunkerImpl.java:101)
    at org.powermock.modules.junit4.common.internal.impl.AbstractCommonPowerMockRunner.run(AbstractCommonPowerMockRunner.java:53)
    at org.powermock.modules.junit4.PowerMockRunner.run(PowerMockRunner.java:53)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:74)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:211)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:67)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)

如果我删除匹配器并在 when 中使用 BaseRepository 对象而不是匹配器,它仍然会引发异常:

org.mockito.exceptions.misusing.MissingMethodInvocationException: 
when() requires an argument which has to be 'a method call on a mock'.
For example:
    when(mock.getArticles()).thenReturn(articles);

Also, this error might show up because:
1. you stub either of: final/private/equals()/hashCode() methods.
   Those methods *cannot* be stubbed/verified.
2. inside when() you don't call method on mock but on some other object.
3. the parent of the mocked class is not public.
   It is a limitation of the mock engine.

-> at com.mycompany.repository.config.MyRepositoryTest.getNextTagNumberTest(MyRepositoryTest.java:39)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:68)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:312)
at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:88)
at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:96)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.executeTest(PowerMockJUnit44RunnerDelegateImpl.java:296)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit49RunnerDelegateImpl$PowerMockJUnit49MethodRunner.executeTestInSuper(PowerMockJUnit49RunnerDelegateImpl.java:116)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit49RunnerDelegateImpl$PowerMockJUnit49MethodRunner.executeTest(PowerMockJUnit49RunnerDelegateImpl.java:77)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runBeforesThenTestThenAfters(PowerMockJUnit44RunnerDelegateImpl.java:284)
at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:86)
at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:49)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.invokeTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:209)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.runMethods(PowerMockJUnit44RunnerDelegateImpl.java:148)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$1.run(PowerMockJUnit44RunnerDelegateImpl.java:122)
at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:33)
at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:45)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.run(PowerMockJUnit44RunnerDelegateImpl.java:120)
at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.run(JUnit4TestSuiteChunkerImpl.java:101)
at org.powermock.modules.junit4.common.internal.impl.AbstractCommonPowerMockRunner.run(AbstractCommonPowerMockRunner.java:53)
at org.powermock.modules.junit4.PowerMockRunner.run(PowerMockRunner.java:53)
at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:74)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:211)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:67)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)

任何帮助将不胜感激——谢谢!


编辑:根据@Ducan 的要求,这里有一些示例代码与我使用的第三方代码非常相似。谢谢!

public class TestRepository {

    private String repoLocation;

    public static TestRepository on (String repoLocation) {
        return new TestRepository(repoLocation);
    }

    public TestRepository(String repoLocation, String extraflags) {
        this.repoLocation = repoLocation;
    }

    private TestRepository (String repoLocation) {
        this(repoLocation, "default flags");
    }
}



@RunWith(PowerMockRunner.class)
@PrepareForTest(TestRepository.class)
public class MyRepositoryTest {

    @Test
    public void staticMockTest() throws IOException {
        PowerMockito.mockStatic(TestRepository.class);
        TestRepository mockRepository = mock(TestRepository.class);
        when(TestRepository.on(anyString())).thenReturn(mockRepository);
    }
}

这不起作用,但给出了与上面不同的异常,我猜这可能是一个根本问题,以某种方式隐藏在上面的问题中:

java.lang.VerifyError: Inconsistent stackmap frames at branch target 51 in method com.mycompany.repository.config.TestRepository.<init>(Ljava/lang/String;)V at offset 41
    at java.lang.Class.getDeclaredConstructors0(Native Method)
    at java.lang.Class.privateGetDeclaredConstructors(Class.java:2413)
    at java.lang.Class.getDeclaredConstructors(Class.java:1855)
    at org.mockito.internal.creation.jmock.ClassImposterizer.setConstructorsAccessible(ClassImposterizer.java:75)
    at org.mockito.internal.creation.jmock.ClassImposterizer.imposterise(ClassImposterizer.java:70)
    at org.powermock.api.mockito.internal.mockcreation.MockCreator.createMethodInvocationControl(MockCreator.java:110)
    at org.powermock.api.mockito.internal.mockcreation.MockCreator.mock(MockCreator.java:60)
    at org.powermock.api.mockito.PowerMockito.mockStatic(PowerMockito.java:70)
    at com.mycompany.MyRepositoryTest.staticMockTest(MyRepositoryTest.java:30)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:68)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:312)
    at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:88)
    at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:96)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.executeTest(PowerMockJUnit44RunnerDelegateImpl.java:296)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit49RunnerDelegateImpl$PowerMockJUnit49MethodRunner.executeTestInSuper(PowerMockJUnit49RunnerDelegateImpl.java:116)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit49RunnerDelegateImpl$PowerMockJUnit49MethodRunner.executeTest(PowerMockJUnit49RunnerDelegateImpl.java:77)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runBeforesThenTestThenAfters(PowerMockJUnit44RunnerDelegateImpl.java:284)
    at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:86)
    at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:49)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.invokeTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:209)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.runMethods(PowerMockJUnit44RunnerDelegateImpl.java:148)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$1.run(PowerMockJUnit44RunnerDelegateImpl.java:122)
    at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:33)
    at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:45)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.run(PowerMockJUnit44RunnerDelegateImpl.java:120)
    at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.run(JUnit4TestSuiteChunkerImpl.java:101)
    at org.powermock.modules.junit4.common.internal.impl.AbstractCommonPowerMockRunner.run(AbstractCommonPowerMockRunner.java:53)
    at org.powermock.modules.junit4.PowerMockRunner.run(PowerMockRunner.java:53)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:74)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:211)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:67)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)

进展?如果我将它设置为 on() 调用一个不调用另一个构造函数 this() 的构造函数,它不会给我这个错误。

【问题讨论】:

  • 您能否简化您的代码示例,使其不依赖于我们没有的外部类?这将帮助我们进行实验以找到解决方案。
  • 对于最后一个异常“java.lang.VerifyError: Inconsistent stackmap frames”,解决方法是在JVM运行时配置中添加-XX:-UseSplitVerifier。请参阅此 stackoverflow 问题:stackoverflow.com/questions/15253173/…

标签: java unit-testing mockito powermock javahg


【解决方案1】:

最近我在尝试测试我的源代码时遇到了类似的问题,其中使用了来自外部类的静态方法。 我使用 EasyMockPowerMock 解决了这个问题。

@RunWith(PowerMockRunner.class)
@PowerMockIgnore("javax.management.*")
@PrepareForTest({ManagementFactory.class, InstallerAppCheckerManagerImpl.class})
public class AppCheckerTester {

  @Test 
  public void testBeanManager() throws Exception {
    // this action is required by PowerMock
    PowerMock.mockStatic(ManagementFactory.class);

    // here is where a mock for static method is defined
    MBeanServer server = PowerMock.createMock(MBeanServer.class);
    EasyMock.expect(ManagementFactory.getPlatformMBeanServer()).andReturn(server);

    // ... 
    PowerMock.replayAll();

    /* some test logic between replay and verify. */

    PowerMock.verifyAll();
  }

}

@PowerMockIgnore 将某个包的加载推迟到父类加载器。在这种情况下使用来自 javax.management 的静态方法:

ManagementFactory.getPlatformMBeanServer()

@PrepareForTest 用于配置您的类和外部类。

希望这些想法对您的测试用例有所帮助。

【讨论】:

  • 为什么同时需要 - EasyMock 和 PowerMock
【解决方案2】:

我相信错误出在这条线上:

LogCommand mockLogCommand = mock(LogCommand.class);

这似乎是从Mockito 类调用mock(Class) 方法(由于静态导入),而它应该来自PowerMockito

【讨论】:

  • 感谢您的回答。我尝试在这里专门使用 PowerMockito.mock() ,但似乎没有修复它。这个 LogCommand 模拟是一个常规的对象模拟,我试图从静态 on() 方法返回,该方法基本上只是一个静态工厂方法。
【解决方案3】:

您的 PowerMock 设置似乎不完整。

  1. 包括以下三个依赖:

    testCompile 'org.powermock:powermock-api-mockito:1.6.2'

    testCompile 'org.powermock:powermock-module-junit4-rule:1.6.2'

    testCompile 'org.powermock:powermock-classloading-xstream:1.6.2'

  2. 将以下规则作为字段添加到您的单元测试类:

    @Rule public PowerMockRule rule = new PowerMockRule();

  3. 另外将以下注释添加到您的测试类:

    @PowerMockIgnore("org.mockito.*")

然后按照Mocking Static Methods 上的说明进行操作。

【讨论】:

    【解决方案4】:
    MockitoAnnotations.initMocks(this);
    

    在您的 setupMethod 上执行此操作以启用注释

    @Before
        public void setUp() throws Exception {
            MockitoAnnotations.initMocks(this);
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-02-01
      • 2012-08-25
      • 1970-01-01
      • 2023-02-04
      • 1970-01-01
      • 2021-11-27
      • 2021-09-13
      相关资源
      最近更新 更多