简体   繁体   中英

Mocking private methods used in public method with PowerMock, Mockito, and TestNG

I want to write a unit test for a class I have. This class has a public method, and inside the public method there are calls to private methods in the same class. I want to mock the calls to those private methods. The class is similar to this:

public class SomeClass {
    public int somePublicMethod(int num) {
        int num2 = somePrivateMethod1(num);
        int num3 = somePrivateMethod2(num);

        return num2 + num3;
    }

    private int somePrivateMethod1(int num) {
        return 2*num;
    }

    private int somePrivateMethod2(int num) {
        return 3*num;
    }
}

For my unit test I am trying to use PowerMock with Mockito and TestNG. Here is my attempt at a test that tests somePublicMethod:

import static org.powermock.api.mockito.PowerMockito.doReturn;
import static org.powermock.api.mockito.PowerMockito.spy;

import org.powermock.core.classloader.annotations.PrepareForTest;
import org.testng.Assert;
import org.testng.annotations.Test;

@PrepareForTest(SomeClass.class)
public class SomeClassTest {

    @Test
    public void testSomePublicMethod() throws Exception {
        int num = 4;        

        SomeClass someClassSpy = spy(new SomeClass());

        doReturn(8).when(someClassSpy, "somePrivateMethod1", num);
        doReturn(12).when(someClassSpy, "somePrivateMethod2", num);

        int result = someClassSpy.somePublicMethod(num);

        Assert.assertEquals(result, 20);
    }
}

When I run this test I get an exception and some hints:

FAILED: testSomePublicMethod
org.mockito.exceptions.misusing.UnfinishedStubbingException: 
Unfinished stubbing detected here:
-> at org.powermock.api.mockito.internal.PowerMockitoCore.doAnswer(PowerMockitoCore.java:31)

E.g. thenReturn() may be missing.
Examples of correct stubbing:
    when(mock.isOk()).thenReturn(true);
    when(mock.isOk()).thenThrow(exception);
    doThrow(exception).when(mock).someVoidMethod();
Hints:
 1. missing thenReturn()
 2. you are trying to stub a final method, you naughty developer!

I've looked at some examples online but I haven't found one that uses PowerMock with Mockito and TestNG specifically to do what I want. Can someone give me some pointers on what I could do differently?

I don't think this is a problem with PowerMock. It looks like it should work just fine as is. I wonder if there is a problem with TestNG interacting with PowerMock?

I have no experience with TestNG, so I tried running the class in JUnit. SomeClass was left as is, SomeClassTest was minimally modified to use JUnit:

import static org.powermock.api.mockito.PowerMockito.*;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)
@PrepareForTest(SomeClass.class)
public class SomeClassTest {

    @Test
    public void testSomePublicMethod() throws Exception {
        final int num = 4;

        final SomeClass someClassSpy = spy(new SomeClass());

        doReturn(8).when(someClassSpy, "somePrivateMethod1", num);
        doReturn(12).when(someClassSpy, "somePrivateMethod2", num);

        final int result = someClassSpy.somePublicMethod(num);

        Assert.assertEquals(result, 20);
    }
}

The test passes.

Do you need to specify a PowerMockRunner.class using @RunWIth or some similar annotation, as with JUnit? If I remove that annotation I recieve the same error message you posted.

EDIT:

According to this link: http://code.google.com/p/powermock/wiki/TestNG_usage

You need to tell TestNG to use the PowerMock object factory. To do this programmatically, add a method like this to your test class:

@ObjectFactory
public IObjectFactory getObjectFactory() {
    return new org.powermock.modules.testng.PowerMockObjectFactory();
}

or to be on the safe side you can also extend from the PowerMockTestCase:

@PrepareForTest(SomeClass.class)
public class SomeClassTest extends PowerMockTestCase {
   //...
}

You can't do by that way. Did you try to use reflection?

By using reflection, you can mock the private methods by mocking the invoke methods, or simpler: you can change it to public temporary, then after the test (maybe in the tearDown) - you can change it back to private.

Hope this help.

I think you are hiting a Mockito limit here. In similar cases, I use Powermock method replacement or, declare the target method as protected and override it when constructing the tested class instance. Hope that helps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM