TestNG+EasyMock: Why @Mock annotation doesn't work without @PrepareForTest?

569 views
Skip to first unread message

Alexander Filipchik

unread,
Mar 7, 2012, 3:52:07 PM3/7/12
to powe...@googlegroups.com
I have a very basic test:
@Test(groups = {"unit"})
@PrepareForTest(StatelessKnowledgeSession.class)
public class GridNodeFactoryTest extends BasicTest {

  private final String SESSION_NAME = "ksession";

  private GridNodeFactory gridNodeFactory;

  @Mock
  private StatelessKnowledgeSession sessionMock;

  @BeforeTest
  public void setUp() {
    gridNodeFactory = new GridNodeFactory();
  }

 @ObjectFactory
  public IObjectFactory getObjectFactory() {
    return new PowerMockObjectFactory();
  }

  @Test
  public void testCreateGridNode() {
    Assert.assertNotNull(sessionMock);
  }
}

StatelessKnowledgeSession is interface in org.drools.runtime package.
In  this example test works fine, but if I remove @PrepareForTest(StatelessKnowledgeSession.class) it will fail because sessionMock will be null.
I thought that @Mock should work fine on interfaces, but it look like it doesn't Mock my interfaces if it is not specified in PrepareForTest.

Is it expected behavior or I'm missing something?

Alex

Johan Haleby

unread,
Mar 8, 2012, 2:20:35 AM3/8/12
to powe...@googlegroups.com
Hi, 

Your observation is true. The reason is that only classes annotated with @PrepareForTest or @SuppressStaticInitializationFor are loaded by the PowerMockObjectFactory which allows the injection to take place. This is because we don't want tests that have nothing to do with PowerMock to be loaded by our classloader and be byte-code manipulated.

Regards,
/Johan

--
You received this message because you are subscribed to the Google Groups "PowerMock" group.
To view this discussion on the web visit https://groups.google.com/d/msg/powermock/-/N_-8FrU4KfYJ.
To post to this group, send email to powe...@googlegroups.com.
To unsubscribe from this group, send email to powermock+...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/powermock?hl=en.

Alexander Filipchik

unread,
Mar 8, 2012, 3:03:09 PM3/8/12
to powe...@googlegroups.com

Alexander Filipchik

unread,
Mar 8, 2012, 6:29:04 PM3/8/12
to powe...@googlegroups.com
I guess I missing something.
Consider second example:

@Test(groups = {"unit"})
@PrepareForTest({Exchange.class, Message.class})
public class DynamicRouteProcessorTest extends BaseTest {
  static final String MOCK_LOCATION = "location";

  @Mock Exchange mockExchange;
  @Mock Message mockMessage;

  private DynamicRouteProcessor processor;

  @BeforeMethod
  public void setUp() {
    processor = new DynamicRouteProcessor();
  }

  @Test
  public void testProcessWithLocationHeader() throws Exception {
    expect(mockExchange.getIn()).andReturn(mockMessage).anyTimes();
    expect(mockMessage.getHeader(DynamicRouteProcessor.LOCATION_HEADER))
        .andReturn(MOCK_LOCATION);
    mockMessage.setHeader(DynamicRouteProcessor.BETFAIR_ROUTE, MOCK_LOCATION);
    replayAll();

    processor.process(mockExchange);
  }

  @Test
  public void testProcessWithoutLocationHeader() throws Exception {
    expect(mockExchange.getIn()).andReturn(mockMessage).anyTimes();
    expect(mockMessage.getHeader(DynamicRouteProcessor.LOCATION_HEADER))
        .andReturn(null);
    mockMessage.setHeader(DynamicRouteProcessor.BETFAIR_ROUTE, MOCK_LOCATION);
    replayAll();

    processor.setLoc(MOCK_LOCATION);
    processor.process(mockExchange);
  }
}

This test fails with:
testProcessWithoutLocationHeader "no last call on a mock available"

And the reason here - mocks were not reinitialized for a second test. If I delete testProcessWithLocationHeader test, second one will pass.
What is wrong?

Alex



On Wed, Mar 7, 2012 at 11:20 PM, Johan Haleby <johan....@gmail.com> wrote:

Johan Haleby

unread,
Mar 12, 2012, 3:20:41 AM3/12/12
to powe...@googlegroups.com
Sorry for the late response. The example you point to in the PowerMock trunk has a PowerMockObjectFactory configured in the suite.xml file in the project, that's why it works. I think what you're experiencing in your example though must be a bug. I tried it out briefly myself and I can reproduce it. Could you add this issue as a bug at the web page (and better yet help out solving it by providing a patch). A work-around is to extend from PowerMockTestCase.

/Johan

Alexander Filipchik

unread,
Mar 13, 2012, 2:30:52 PM3/13/12
to powe...@googlegroups.com
I added new bug and also I found pretty old defect:
Issue 213: TestNG runner probably clears too much state after each test method
which says:
"TestNG has much more features than JUnit so our standard approach of
clearing the whole MockRepository after each test case doesn't hold up for
TestNG. For example testng may instantiate mocks in BeforeClass method
which should not be removed from the repo until the end of class. This is
not supported today."
I guess, If it is implemented it causes my problems. The good question is - how to force testng to clear state BeforeMethod if it is required.

Johan Haleby

unread,
Mar 13, 2012, 3:13:06 PM3/13/12
to powe...@googlegroups.com
Hmm I actually don't think issue 213 has anything to do with it. What's happening (if I remember everything correctly) is that if the test case doesn't extend from PowerMockTestCase it's  byte-code manipulated to do so. PowerMockTestCase takes care of injecting mocks as well as cleaning up state etc.

/Johan

Alexander Filipchik

unread,
Mar 13, 2012, 4:28:00 PM3/13/12
to powe...@googlegroups.com
Interesting, I found that extending my test from PowerMockTestCase fixes the problem.
So, are we seeing issues with "byte-code manipulated to do so"?

Alex

Johan Haleby

unread,
Mar 14, 2012, 4:30:40 AM3/14/12
to powe...@googlegroups.com
I think so too. Don't know why though, it needs to be be investigated. 

/Johan
Reply all
Reply to author
Forward
0 new messages