samedi 30 janvier 2016

Unexpected method call, but only with a PartialMock...?

I'm tearing my hair out trying to figure out why I can successfully establish method call expectations on a mock object (using PowerMock) when I create a regular mock (i.e. by calling PowerMock.createMock()), but not when I use a partialMock (by calling PowerMock.createPartialMock()) and adding the methods to be mocked.

The code below shows two tests (I'm not asserting anything here, just demonstrating the failure). The first one runs with no errors. The second is exactly like the first, except I create a partial mock. The second test throws an Assertion error (text of error at bottom of post) claiming an unexpected method call to "Bluetooth.getName()", as though I didn't setup the expectation properly. But I use the same expectation setup as in the first test!

I'm somewhat new to Partial Mocks, so maybe I'm missing something, but I've setup my code according to PowerMock's docs and the numerous examples that I've examined.

Note that the class that I'm trying to mock is the Android BluetoothDevice class. It's a final class (which is why I'm using PowerMock). I'm not sure if that matters (and can't imagine why that would work with a normal mock but not a partial mock), but I thought I'd mention it just in case.

Thanks for any help.

public class PartialMockTests
{
@Test
public void testNormalMock()
{
    BluetoothDevice normalMock = PowerMock.createMock(BluetoothDevice.class);

    // tell EasyMock to expect call to mocked getAddress()
    EasyMock.expect(normalMock.getName()).andReturn("fakeName");
    EasyMock.expectLastCall().anyTimes();
    EasyMock.replay(normalMock);

    //Exercise the mock
    normalMock.getName(); // No error here!

    EasyMock.verify(normalMock);
}

@Test
public void testPartialMock()
{
    BluetoothDevice partialMock =
            PowerMock.createPartialMock(
                    BluetoothDevice.class,
                    "getName", "toString");  //If I don't mock "toString", I get a NPE

    // tell EasyMock to expect call to mocked getAddress()
    EasyMock.expect(partialMock.getName()).andReturn("fakeName");
    EasyMock.expectLastCall().anyTimes();
    EasyMock.replay(partialMock);

    //Exercise the mock
    partialMock.getName(); // Now I get a Assertion Error:  Unexpected Method Call!  Why?

    EasyMock.verify(partialMock);
}
}

Here is the Assertion Error text: java.lang.AssertionError: Unexpected method call BluetoothDevice.getName(): at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:44) at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:94) at org.easymock.internal.ClassProxyFactory$MockMethodInterceptor.intercept(ClassProxyFactory.java:97) at android.bluetooth.BluetoothDevice$$EnhancerByCGLIB$$6c62bd71.getName() at my.domain.package.PartialMockTests.testPartialMock(PartialMockTests.java:40) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:78) at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:212) at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:68) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)

Aucun commentaire:

Enregistrer un commentaire