PowerMock静态方法存根似乎不起作用

时间:2015-02-25 02:29:10

标签: java unit-testing mockito powermock javahg

我做了一堆谷歌搜索和搞乱我的代码但最终我似乎无法弄清楚为什么静态模拟不适用于PowerMock和Mockito。

我试图在JavaHg库https://bitbucket.org/aragost/javahg中模拟LogCommandon()方法,该方法将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而不是matcher中使用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()的构造函数,它就不会给我这个错误。

4 个答案:

答案 0 :(得分:1)

最近我有一个类似的问题试图测试我的源代码,其中使用了来自外部类的静态方法。 我使用 EasyMock PowerMock 解决了这个问题。

@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 用于配置您的类和外部类。

希望这些想法可以帮助您完成测试用例。

答案 1 :(得分:1)

我相信错误就在行中:

LogCommand mockLogCommand = mock(LogCommand.class);

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

答案 2 :(得分:0)

您的 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.*")

  4. 然后按照Mocking Static Methods上的说明操作。

答案 3 :(得分:0)

MockitoAnnotations.initMocks(this);

在setupMethod上执行此操作以启用anotations

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