Tarun Kumar
Tarun Kumar

Reputation: 5230

Unit testing with mockito for constructors

I have one class.

Class First {

    private Second second;

    public First(int num, String str) {
        second = new Second(str);
        this.num = num;
    }

    ... // some other methods
}

I want to write unit tests for public methods of class First. I want to avoid execution of constructor of class Second.

I did this:

Second second = Mockito.mock(Second.class);
Mockito.when(new Second(any(String.class))).thenReturn(null);
First first = new First(null, null);

It is still calling constructor of class Second. How can i avoid it?

Upvotes: 59

Views: 205438

Answers (7)

distante
distante

Reputation: 7005

Mockito can now mock constructors (since version 3.5.0) https://javadoc.io/static/org.mockito/mockito-core/3.5.13/org/mockito/Mockito.html#mocked_construction

try (MockedConstruction mocked = mockConstruction(Foo.class)) {
   Foo foo = new Foo();
   when(foo.method()).thenReturn("bar");
   assertEquals("bar", foo.method());
   verify(foo).method();
 }

Upvotes: 11

Rishabh Sachdeva
Rishabh Sachdeva

Reputation: 91

I believe, it is not possible to mock constructors using mockito. Instead, I suggest following approach

Class First {

   private Second second;

   public First(int num, String str) {
     if(second== null)
     {
       //when junit runs, you get the mocked object(not null), hence don't 
       //initialize            
       second = new Second(str);
     }
     this.num = num;
   }

   ... // some other methods
}

And, for test:

class TestFirst{
    @InjectMock
    First first;//inject mock the real testable class
    @Mock
    Second second

    testMethod(){

        //now you can play around with any method of the Second class using its 
        //mocked object(second),like:
        when(second.getSomething(String.class)).thenReturn(null);
    }
}

Upvotes: 1

Ramesh V
Ramesh V

Reputation: 11

Include this line on top of your test class

@PrepareForTest({ First.class })

Upvotes: 0

Rocky Mena
Rocky Mena

Reputation: 192

Here is the code to mock this functionality using PowerMockito API.

Second mockedSecond = PowerMockito.mock(Second.class);
PowerMockito.whenNew(Second.class).withNoArguments().thenReturn(mockedSecond);

You need to use Powermockito runner and need to add required test classes (comma separated ) which are required to be mocked by powermock API .

@RunWith(PowerMockRunner.class)
@PrepareForTest({First.class,Second.class})
class TestClassName{
    // your testing code
}

Upvotes: 18

terma
terma

Reputation: 1199

You can use PowerMockito

See the example:

Second second = Mockito.mock(Second.class);
whenNew(Second.class).withNoArguments().thenReturn(second);

But re-factoring is better decision.

Upvotes: 77

Velibor Dragutinović
Velibor Dragutinović

Reputation: 207

I have used "Pattern 2 - the "factory helper pattern"

Pattern 2 - the factory helper pattern

One case where this pattern won't work is if MyClass is final. Most of the Mockito framework doesn't play particularly well with final classes; and this includes the use of spy(). Another case is where MyClass uses getClass() somewhere, and requires the resulting value to be MyClass. This won't work, because the class of a spy is actually a Mockito-generated subclass of the original class.

In either of these cases, you'll need the slightly more robust factory helper pattern, as follows.

public class MyClass{
  static class FactoryHelper{
      Foo makeFoo( A a, B b, C c ){
          return new Foo( a, b, c );
      }
  }

  //...

  private FactoryHelper helper;
  public MyClass( X x, Y y ){
      this( x, y, new FactoryHelper());
  } 

  MyClass( X x, Y, y, FactoryHelper helper ){

      //...

      this.helper = helper;
  } 

  //...

  Foo foo = helper.makeFoo( a, b, c );
}

So, you have a special constructor, just for testing, that has an additional argument. This is used from your test class, when creating the object that you're going to test. In your test class, you mock the FactoryHelper class, as well as the object that you want to create.

@Mock private MyClass.FactoryHelper mockFactoryHelper;
@Mock private Foo mockFoo;
private MyClass toTest;

and you can use it like this

toTest = new MyClass( x, y, mockFactoryHelper ); 
when( mockFactoryHelper.makeFoo( 
  any( A.class ), any( B.class ), any( C.class )))
  .thenReturn( mockFoo ); 

Source: http://web.archive.org/web/20160322155004/http://code.google.com/p/mockito/wiki/MockingObjectCreation

Upvotes: 5

Tomasz Nurkiewicz
Tomasz Nurkiewicz

Reputation: 340733

Once again the problem with unit-testing comes from manually creating objects using new operator. Consider passing already created Second instead:

class First {

  private Second second;

  public First(int num, Second second) {
    this.second = second;
    this.num = num;
  }

  // some other methods...
}

I know this might mean major rewrite of your API, but there is no other way. Also this class doesn't have any sense:

Mockito.when(new Second(any(String.class).thenReturn(null)));

First of all Mockito can only mock methods, not constructors. Secondly, even if you could mock constructor, you are mocking constructor of just created object and never really doing anything with that object.

Upvotes: 36

Related Questions