如何使用PowerMockito模拟私有方法的任何参数?

时间:2014-09-25 15:07:24

标签: methods junit mockito private powermock

我正在使用Mockito 1.9.5,PowerMock 1.5.1,JUnit 4.11和Spring 3.1.4.RELEASE。我正在尝试编写一个JUnit测试,其中我想模拟一个什么都不做的私有方法。私有方法签名是

public class MyService
{

    …
    private void myMethod(byte[] data, UserFile userFile, User user)
    {

在我的JUnit测试中,我有

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({ "classpath:test-context.xml" })
@PrepareForTest(MyServiceImpl.class)
public class MyServiceIT 
{

    @Rule
    public PowerMockRule rule = new PowerMockRule();

    @Autowired
    private MyService m_mySvc;
    private MyService m_mySvcSpy;

    @Before
    public final void setup() throws Exception
    {
        m_mySvcSpy = PowerMockito.spy(m_mySvc);
        PowerMockito.doNothing().when(m_mySvcSpy, “myMethod”, Matchers.any(byte[].class), Matchers.any(UserFile.class), Matchers.any(User.class));

不幸的是,第二行死于异常

testUploadFile(org.mainco.subco.user.service.MyServiceIT)  Time elapsed: 12.693 sec  <<< ERROR!

org.powermock.reflect.exceptions.MethodNotFoundException:在类org.mainco.subco.user.service.UserFileService $$ EnhancerByMockitoWithCGLIB $$ 4中找不到名称为'myMethod'且参数类型为[null,null,null]的方法e52bc77。         at org.powermock.reflect.internal.WhiteboxImpl.throwExceptionIfMethodWasNotFound(WhiteboxImpl.java:1247)         at org.powermock.reflect.internal.WhiteboxImpl.findMethodOrThrowException(WhiteboxImpl.java:985)         at org.powermock.reflect.internal.WhiteboxImpl.doInvokeMethod(WhiteboxImpl.java:882)         在org.powermock.reflect.internal.WhiteboxImpl.invokeMethod(WhiteboxImpl.java:713)         在org.powermock.reflect.Whitebox.invokeMethod(Whitebox.java:401)         在org.powermock.api.mockito.internal.expectation.PowerMockitoStubberImpl.when(PowerMockitoStubberImpl.java:93)         在org.mainco.subco.user.service.MyServiceIT.setup(UserFileServiceIT.java:42)

使用PowerMockito模拟私有方法的泛型参数的正确方法是什么?

6 个答案:

答案 0 :(得分:1)

我还没有对它进行测试,但我认为找不到私有方法的原因是它在Spring提供的对象中不存在 MyService界面。此对象是代理,生成的类的实例,不扩展MyServiceImpl;所以,其中没有private myMethod(...)

为了避免这个问题,测试应该不使用Spring。而是直接在MyServiceImpl方法中实例化setup()。如果测试需要其他依赖项注入m_mySvc,那么为那些创建模拟并让PowerMock注入它们。

答案 1 :(得分:1)

在我的情况下,我希望私有方法能够&#34;返回&#34;什么,所以这就是我做的事情

        MyTestClass spyInstance = spy(MyTestClass.getNewInstance());
        MyGenericClass obj = mock(MyGenericClass.class);

           doReturn(obj).when(spyInstance,method(MyTestClass.class,"privateMethod",
            String.class,HashMap.class,Class.class)).
            withArguments("624",null,MyGenericClass.class);`

我的私人方法是......

private <T> T privateMethod(String str,HashMap<String,String> map,Class<T> c)

答案 2 :(得分:0)

我猜你的测试是用SpringJUnit4ClassRunner注释的。在这种情况下,您必须使用PowerMockRule

public class MyServiceTest {
    @Rule
    public PowerMockRule rule = new PowerMockRule();
    ...

此外,你正在使用powermock来解释MyService的方法。因此,您必须使用@PrepareForTest注释测试类:

...
@PrepareForTest(MyServiceImpl.class)
public class MyServiceTest {
    ...

完整代码:

@PrepareForTest(MyServiceImpl.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class MyServiceTest {
    @Rule
    public PowerMockRule rule = new PowerMockRule();

    @Autowired
    private MyService service;
    private MyService spy;

    @Before
    public final void setup() throws Exception {
        spy = PowerMockito.spy(service);
        PowerMockito.doNothing().when(spy,
                "myMethod",
                Matchers.any(byte[].class),
                Matchers.any(UserFile.class),
                Matchers.any(User.class));
    }

    ...
}

您可以看到a full example of spying in the documentation.

答案 3 :(得分:0)

你的问题的前提似乎暗示你违反了Single Responsibility Principle (SRP);如果你需要测试私有方法,并且为了做到这一点,你需要将东西注入其中,你应该考虑将它移到另一个服务。

我建议您执行以下操作:

public class MyService implements IService { // I know it's not a strong convention in
                                             // the Java world, but I'm primarily a C#
                                             // guy, so I tend to give any interface a
                                             // name that starts with captital "I"

    public MyService(IDependencyOne dep1, IDependencyTwo dep2) {
        // ...
    }

    public Stuff getTheStuffThatAnIServiceGets() {
        // your previously private method was used here before
        // Now, it's instead a method defined on the IDependencyTwo interface,
        // so you have it available as _dep2.myMethod(...)
    }
}

public class OtherService implements IDependencyTwo {
    public void myMethod(/* possibly some arguments... */) {
        // ...
    }
}

通过这种分离,你可以在测试IDependencyTwo时注入一些嘲弄先前私有方法(只是模拟MyService)的行为,同样你可以模拟你需要注入的任何内容通过模拟和注入IDependencyTwo的依赖关系进入以前的私有方法。

答案 4 :(得分:0)

因为你看到了这个例外:

org.powermock.reflect.exceptions.MethodNotFoundException: No method found with name ‘myMethod’ with parameter types: [ null, null, null ] in class

您可以尝试使用mockito的isNull匹配器(并且可能使用某种类型转换器)吗?

换句话说,你改为使用

PowerMockito.doNothing().when(m_mySvcSpy, "myMethod", Matchers.isNull(byte[].class), Matchers.isNull(UserFile.class), Matchers.isNull(User.class));

顺便说一句,你可以static import Matchers.isNull,这样你就可以简单地使用isNull代替Matchers.isNull

答案 5 :(得分:0)

我继续使用“压制”......

suppress(method(MyServiceImpl.class,  “myMethod”));