PowerMock通过whenNew()模拟构造函数无法使用匿名类。

23
我有一个DummyResource类和一个DummyTarget文件,还有一个如下的测试类TestDummyResource,但是模拟对象`DummyResource dr = mock(DummyResource.class)`只能在正常类中调用构造函数时才起作用,当在匿名类中调用时,它会调用实际的构造函数而不是使用模拟对象。
版本: powermock 1.4.12 mockito 1.9.0 junit 4.8.2
DummyTarget.java:
import java.io.IOException;
import java.io.OutputStream;

import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.StreamingOutput;


public class DummyTarget {
    public StreamingOutput testMocking() {
        return new StreamingOutput() {
            @Override
            public void write(OutputStream arg0) throws IOException, WebApplicationException {
                new DummyResource();
            }
        };
    }
}

DummyResource.java:

package com.smin.dummy;

public class DummyResource {
    public DummyResource() {
        System.out.println("mock failure");
    }
}

TestDummyResource.java:

package com.smin.dummy;

import static org.mockito.Mockito.mock;

import java.io.IOException;

import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.StreamingOutput;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)
@PrepareForTest({DummyResource.class,DummyTarget.class})
public class TestDummyResource  {

    @Before
    public void setUp() throws Exception {
        DummyResource dr = mock(DummyResource.class);
        PowerMockito.whenNew(DummyResource.class).withNoArguments().thenReturn(dr);
    }

    @Test
    public void testMocked() throws WebApplicationException, IOException {
        new DummyResource(); // it uses the mocked object dr here, 
                             //doesn't print "mock failure"
        StreamingOutput sop = new DummyTarget().testMocking();
        sop.write(null);     // it calls DummyResource's constructor,
                             // prints ""mock failure"" here
    }
}
4个回答

39

您需要准备好调用构造函数的类,而不是在构造函数被调用的类中,以下代码应该可以解决问题:

@PrepareForTest(DummyTarget.class)

更多信息请查看页面。


那真是让人头疼,我不确定是否可以(使用PowerMock)来模拟匿名类中的构造函数。如果你能改变源代码,也许可以将其修改为私有类来使其正常工作。 - zbrunson
我已经更新了上面的代码,如果构造函数在匿名类中被调用,是否有任何解决方法? - Shengjie
我还没有阅读完整篇文章,但是这个问题或许可以帮到你。 - zbrunson
我的情况很简单,一个服务正在创建另一个类的实例。因此,我必须为测试准备好我的服务实现。它起作用了。 - will824
我也花了一些时间。因为参考资料已经过时了。我只是更新了它。https://github.com/jayway/powermock/wiki/MockConstructor 它说:在测试用例的类级别上使用@PrepareForTest(ClassThatCreatesTheNewInstance.class)注释。 - Victor Choy
显示剩余2条评论

17

看起来匿名类可能会继承定义它的类的包。你能否尝试PrepareForTest的通配符形式吗?

@PrepareForTest("com.smin.dummy.*")

如果那个不起作用,你可以尝试使用霰弹枪PrepareEverythingForTest注释。


1
准备测试所需的一切,但是运行时间太长了。 - Shengjie
3
好的,除了 API 可能因为这篇文章发布后而有所更改之外其他都完美无误。使用 @PrepareForTest(fullyQualifiedNames = "com.smin.dummy.*") - Matt Byrne
@Brian 这个链接已经失效了。 - NIKHIL CHAURASIA

10

3
这实际上是正确答案,而不是上面的那个答案。 - user590849
对我有用。谢谢。 - defactodeity

0

我曾经遇到过同样的问题,通过使用whenNew和完全限定名解决了它。在你的情况下,内部匿名类的完全限定名是:

DummyTarget.class + "$1"

所以你应该创建一个该类的模拟对象:
DummyResource dr = mock(Class.forName(DummyTarget.class + "$1"));

这样做对你很有帮助。

另外,不要忘记准备DummyTarget类:

@PrepareForTest(DummyTarget.class)

希望它有所帮助 =]

部分工作完成。我已经能够创建一个匿名类的模拟对象,并将其与实际代码链接起来。但是该对象始终为null。 - Sagar Trehan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接