3

My problem seems to be with some misconception with mocking. The code for test.

public class CallHandler {
    private SqlSessionFactory sessionFactory;
    public CallHandler() {
        String resource = "mybatis/mybatis-config.xml";
        Reader reader;
        try {
            reader = Resources.getResourceAsReader(resource);
            sessionFactory = new SqlSessionFactoryBuilder().build(reader);
        }
        catch (IOException e) {
            e.printStackTrace();
        }
    }

    public String handleRequest(Call call) {
        // Some Implementation
    }
}

The test class

// I have excluded few unnecessary classes from @PrepareForTest for the post.

@RunWith(PowerMockRunner.class)
@PrepareForTest({SqlSessionFactoryBuilder.class, Resources.class, SqlSessionFactory.class, SqlSession.class})
public class TestCase0 extends TestCase{
    private SqlSessionFactory mockedSessionFactory = PowerMock.createMock(SqlSessionFactory.class);
    private SqlSession mockedSession = PowerMock.createMock(SqlSession.class);
    private CallMapper mockedMapper = PowerMock.createMock(CallMapper.class);
    private SqlSessionFactoryBuilder mockedSqlSessionFactoryBuilder= PowerMock.createMock(SqlSessionFactoryBuilder.class);
    // Others

    @Before
    public void setUp() {
    }

    @Test
    public void test0 () throws Exception {
        mockStatic(CallMapper.class);
        mockStatic(SqlSessionFactoryBuilder.class);
        mockStatic(Resources.class);
        expect(Resources.getResourceAsReader("mybatis/mybatis-config.xml")).andReturn(mockedReader);      expectNew(SqlSessionFactoryBuilder.class).andReturn(mockedSqlSessionFactoryBuilder);  expect(mockedSqlSessionFactoryBuilder.build(mockedReader)).andReturn(mockedSessionFactory);
        expect(mockedSessionFactory.openSession()).andReturn(mockedSession);
        // Few more expectations
        replayAll();
        assertThat(RESULT0).isEqualTo((new CallHandler()).handleRequest(call));
        verifyAll();
    }
}

This mocking of call of build on the new SqlSessionFactoryBuilder does not seem to be taking effect because of which the original build() is being called and hence the error. The stack trace

java.lang.AssertionError: 
Unexpected method call Reader.close();
at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:44)
at org.powermock.api.easymock.internal.invocationcontrol.EasyMockMethodInvocationControl.invoke(EasyMockMethodInvocationControl.java:91)
at org.powermock.core.MockGateway.doMethodCall(MockGateway.java:105)
at org.powermock.core.MockGateway.methodCall(MockGateway.java:168)
at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:58)
at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:38)
at com.kwench.integration.ivr.CallHandler.<init>(CallHandler.java:48)
at in.kwench.integration.ivr.TestCase0.test0(TestCase0.java:131)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:68)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:310)
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:294)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTestInSuper(PowerMockJUnit47RunnerDelegateImpl.java:127)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTest(PowerMockJUnit47RunnerDelegateImpl.java:82)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runBeforesThenTestThenAfters(PowerMockJUnit44RunnerDelegateImpl.java:282)
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:207)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.runMethods(PowerMockJUnit44RunnerDelegateImpl.java:146)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$1.run(PowerMockJUnit44RunnerDelegateImpl.java:120)
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:118)
at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.run(JUnit4TestSuiteChunkerImpl.java:104)
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.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
akhilsurya
  • 33
  • 1
  • 5

2 Answers2

7

Without the full example of the code you're testing and the test class itself, it's difficult to say where you issues could be arising.

Having said that, I'm guessing your issue is that you haven't prepared the classes with the static methods. These need to be provided in the @PrepareForTest annotation at the start of your test class.

The documentation for PowerMock provides the following tips for mocking static methods. I'm guessing you've missed out point 2. Here is the source for this documentation

Mocking static methods

Quick summary

  1. Use the @RunWith(PowerMockRunner.class) annotation at the class-level of the test case.
  2. Use the @PrepareForTest(ClassThatContainsStaticMethod.class) annotation at the class-level of the test case.
  3. Use PowerMock.mockStatic(ClassThatContainsStaticMethod.class) to mock all methods of this class.
  4. Use PowerMock.replay(ClassThatContainsStaticMethod.class) to change the class to replay mode.
  5. Use PowerMock.verify(ClassThatContainsStaticMethod.class) to change the class to verify mode.

You are also mocking the constructor call for SqlSessionFactoryBuilder so you'll need to consider the tips for that too. Here is the documentation for this

Mock construction of new objects

Quick summary

  1. Use the @RunWith(PowerMockRunner.class) annotation at the class-level of the test case.
  2. Use the @PrepareForTest(ClassThatCreatesTheNewInstance.class) annotation at the class-level of the test case.
  3. Use PowerMock.createMock(NewInstanceClass.class) to create a mock object of the class that should be constructed (let's call it mockObject).
  4. Use PowerMock.expectNew(NewInstanceClass.class).andReturn(mockObject) to expect a new construction of an object of type NewInstanceClass.class but instead return the mock object.
  5. Use PowerMock.replay(mockObject, NewInstanceClass.class) to change the mock object and class to replay mode, alternatively use the PowerMock.replayAll() method.
  6. Use PowerMock.verify(mockObject, NewInstanceClass.class) to change the mock object and class to verify mode, alternatively use the PowerMock.verifyAll() method.

With those tips in mind, I produced the following test method for the method you provided that you are trying to test. This test passes fine.

@RunWith(PowerMockRunner.class)
@PrepareForTest({CallHandler.class, SqlSessionFactoryBuilder.class, Resources.class})
public class CallHandlerTest {

    private static final String RESULT0 = "";

    @Test
    public void test0 () throws Exception {
        final Reader mockedReader = EasyMock.createMock(Reader.class);
        final SqlSessionFactoryBuilder mockedSqlSessionFactoryBuilder = EasyMock.createMock(SqlSessionFactoryBuilder.class);
        final SqlSessionFactory mockedSessionFactory = EasyMock.createMock(SqlSessionFactory.class);

        PowerMock.mockStatic(SqlSessionFactoryBuilder.class);
        PowerMock.mockStatic(Resources.class);

        PowerMock.expectNew(SqlSessionFactoryBuilder.class).andReturn(mockedSqlSessionFactoryBuilder);
        EasyMock.expect(Resources.getResourceAsReader("mybatis/mybatis-config.xml")).andReturn(mockedReader);
        EasyMock.expect(mockedSqlSessionFactoryBuilder.build(mockedReader)).andReturn(mockedSessionFactory);

        PowerMock.replayAll();
        assertThat(RESULT0).isEqualTo((new CallHandler()).handleRequest(new Call()));
        PowerMock.verifyAll();
    }
}
Dan Temple
  • 2,736
  • 2
  • 22
  • 39
  • I have changed function to complete class. However, I don't think I need to include the `CallHandler.class` in the `@PreparedTest` because it does not have any static methods – akhilsurya Jun 26 '14 at 05:26
  • The log shows that the error is because of `SqlSessionBuilder.build(..)`, which is present in the given code with enough details. And the order in your test, doesn't that matter ? If it does, then shouldn't `PowerMock.expectNew(...)` be below the first `EasyMock.expect(...)` – akhilsurya Jun 26 '14 at 05:37
  • @user148015 When making an expectation on a constructor call (using `expectNew()`) you need to include the *class that calls the constructor* in the PrepareForTest. So in your case, the `CallHandler` class needs to be prepared for test. See [this other SO post](http://stackoverflow.com/questions/12360555/powermocks-expectnew-isnt-mocking-a-constructor-as-expected) and [the documentation for expectNew()](https://code.google.com/p/powermock/wiki/MockConstructor) for examples of this. I'll add this to my answer as well. – Dan Temple Jun 26 '14 at 06:03
  • Happy days! :) I only learnt that by browsing around for a solution to your problem. Good ol' SO, helping everyone learn yet again. – Dan Temple Jun 26 '14 at 06:38
1

It's the conflict between code coverage and powermock.mockStatic.

Code coverage instruments $jacocoInit static method into all the class to collect the code coverage. When running the testcase, mockStatic mock the $jacocoInit static method which leads to an expected static method call.

You can bypass the problem with creating partial mock on static method, e.g. PowerMock.mockStaticPartial(TheClass.class, "theStaticMethod") so that power mock won't touch the $jacocoInit method.