simba
simba

Reputation: 21

Mocking constructor using powermock on the class which needs to be tested

I am able to mock a constructor call using powermock from inside a class which I want to test. This works when I add the class I want to test in @PrepareForTest. But once I add my class there, even when the test cases pass, the coverage is being shown as 0 in the coverage plugin.

When I remove my class from @PrepareForTest, of course, coverage starts showing up for other test cases but the test case in which I have to mock constructor call fails. Not sure what to do about this.

Class A
{
   MyObject o;
   A(){
     //some other code
     o = new MyObject();
     //some other code
   }

  public void process(){
    //some code

}

@RunWith(PowerMockRunner.class)
@PrepareForTest(A.class)
Class TestA{

  @Test
  public void test1()
  {
    MyObject mocked = Mockito.mock(MyObject.class);
 PowerMockito.whenNew(MyObject.class).withNoArguments().thenReturn(mocked);
  A a = new A();
  a.process();
  //Assert as per test case

 }

}

In coverage tool, coverage shows as 0 however, unit test passes and I checked in debug mode that it was covering all the statements of class A.

Upvotes: 0

Views: 2361

Answers (2)

GhostCat
GhostCat

Reputation: 140447

In coverage tool, coverage shows as 0 however, unit test passes and I checked in debug mode that it was covering all the statements of class A.

Coverage tools rely on manipulating the executed byte code.

So does PowerMock, when you mock static/new.

This can quickly lead to all sorts of problems. For JaCoCo, there seems to be a solution around offline instrumentation. Where, I also remember: some other person asked about that some time back, and in the end gave up, because he couldn't get "offline instrumentation" to work either.

For any other framework, I have to repeat old advice: consider to invest your time to learn how to write easy-to-test code. Because if you would do that, you would not need to use PowerMock(ito) in order to test it.

Your code is hard-to-test because of that new() statement in the constructor. Simply don't do that. Either use dependency injection via @InjectMocks, or have a test-only constructor that takes the required object.

Long story sort: when you write your own, new code, and you think you need PowerMock to test it, then you are doing something wrong.

Upvotes: 2

Maciej Kowalski
Maciej Kowalski

Reputation: 26522

I think you can do without Powermock here. If you Spy on class A and mock the getter you should end up with the same result and most likely have your coverage correct:

@Test
public void test1(){
    MyObject mocked = Mockito.mock(MyObject.class);
    A spyA = Mockito.spy(new A());
    doReturn(mocked).when(spyA).getMyObject();
    ...
}

Upvotes: 0

Related Questions