23

I have a DummyResource class and a DummyTarget file, and a test class TestDummyResource as below, but the mocked object DummyResource dr = mock(DummyResource.class) only works when I call the constructor inside a normal class, when it's called in an anonymous class, it's calling the actual constructor instead of using the mocked object.

Versions:

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
    }
}
Vitaly Olegovitch
  • 3,509
  • 6
  • 33
  • 49
Shengjie
  • 12,336
  • 29
  • 98
  • 139

4 Answers4

39

You need to have prepared the class calling the constructor, not the class on which the constructor is called, the following should fix you up:

@PrepareForTest(DummyTarget.class)

For more information check this page.

zbrunson
  • 1,587
  • 1
  • 12
  • 13
  • my case actually is more complex, The constructor is called in an anonymous class where even have the anonymous class declared in @PrepareForTest doesn't work still. – Shengjie Nov 20 '12 at 20:49
  • That is nasty, I'm not sure it is possible (using PowerMock) to mock the constructor in an anonymous class. If you can change the source you may be able to get it to work if you change it to a private class. – zbrunson Nov 20 '12 at 20:51
  • I've updated my code above, is there any workaround if the constructor is called in the anonymous class? – Shengjie Nov 20 '12 at 21:04
  • I haven't read the full post, but [this question](http://stackoverflow.com/questions/7523389/how-do-i-use-powermockito-to-mock-the-construction-of-new-objects-when-testing-a) may cover you – zbrunson Nov 20 '12 at 21:12
  • My case was simple, a Service that was creating an instance of another class. Therefore I had to prepare my service implementation for the test. It worked. – will824 Apr 29 '13 at 15:56
  • One drawback is the coverage tool does not show me anymore the classed I am testing as covered. – will824 Apr 29 '13 at 16:06
  • I spend a while for this too. Because the reference is outdated. I just update it. https://github.com/jayway/powermock/wiki/MockConstructor It says: Use the @PrepareForTest(ClassThatCreatesTheNewInstance.class) annotation at the class-level of the test case – Victor Choy Mar 01 '16 at 05:10
17

It looks like an anonymous class may inherit the package of the class that defines it. Can you try the wildcard form of PrepareForTest?:

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

If that doesn't work, you could try the shotgun PrepareEverythingForTest Annotation.

Brian Henry
  • 3,161
  • 1
  • 16
  • 17
10

Actually, you have to prepare for test the class that makes the constructor call, not the class on which the constructor was called. See https://github.com/jayway/powermock/wiki/MockConstructor.

In your case, you should use @PrepareForTest(DummyTarget.class)

Magnus Reftel
  • 967
  • 6
  • 19
Hamilton Rodrigues
  • 1,779
  • 1
  • 11
  • 3
0

I had the same problem, and resolved it with using whenNew with fully qualified name. The fully qualified name of an inner anonymous class in your case is:

DummyTarget.class + "$1"

so you should create a mock of that class:

DummyResource dr = mock(Class.forName(DummyTarget.class + "$1"));

and it will work for you.

Also, don't forget to prepare the DummyTarget class:

@PrepareForTest(DummyTarget.class)

Hope it helped =]

Slava Shpitalny
  • 3,965
  • 2
  • 15
  • 22
  • Partially worked. I am able to create a mock object of anonymous class. Also able to linked it with the actual code. But the object is always null. – Sagar Trehan May 19 '17 at 11:54