57

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?

Kalle Richter
  • 8,008
  • 26
  • 77
  • 177
Tarun Kumar
  • 5,150
  • 6
  • 26
  • 30
  • 1
    Possible duplicate of http://stackoverflow.com/questions/6288575/mockito-mocking-legacy-class-constructor – serg10 Jun 26 '12 at 18:57
  • try this answer https://stackoverflow.com/a/60429929/15435022 – mattsmith5 Oct 20 '21 at 20:32
  • Does this answer your question? [Mockito junit 5 mock constructor](https://stackoverflow.com/questions/64905956/mockito-junit-5-mock-constructor) – tkruse Nov 07 '22 at 17:29

7 Answers7

77

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.

try-catch-finally
  • 7,436
  • 6
  • 46
  • 67
terma
  • 1,199
  • 1
  • 8
  • 15
  • 2
    Coverage Notice: If the class under test (A) is the one which is creating a new instance of another class (B), you'll have to add A to "@PrepareForTest" to call whenNew() in this case AND *this will prevent Jacoco from reading the coverage*. – Abhishek Saini Feb 22 '18 at 05:08
36

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.

satanas
  • 706
  • 7
  • 11
Tomasz Nurkiewicz
  • 334,321
  • 69
  • 703
  • 674
  • 17
    But there *is* another way: add PowerMock to the mix so you can mock the `Second` constructor, as the answer by @terma shows. – Rogério Jul 09 '12 at 17:52
  • 3
    @Rogério I agree and disagree. I agree in the sense that it is a viable alternative, but I disagree because the OP is only using Mockito and gives no indication of desire, or even ability to obtain and utilize a separate framework, which is less trivial in the corporate environment than it is in the academic. This should still be the accepted answer based on how the OP worded his question. Besides, to the first point Tomasz has made in his answer, Mockito is written to promote writing code that is testable. Using Powermock indicates you have written untestable code and it needs refactoring. – searchengine27 Oct 31 '17 at 17:13
  • 2
    @searchengine27 Actually, nowhere the question says that adding another testing library would be a problem (it might be, of course). In my experience in real-world projects, adding a *test* library is not a big issue since it's only used for automated tests, not for production code. And Mockito was *not* "written to promote writing code that is testable" - this is just your opinion, for which there is no evidence; give me a reference if you think there is. On the contrary, usage of testing libraries with such arbitrary (and accidental) limitations often prevents proper API design and OO code. – Rogério Nov 01 '17 at 12:59
  • There is another way. He can move the object creation logic into a method and mock/override this method for testing. – Muhammad Gelbana Dec 26 '18 at 11:10
  • So, if there is would be a method call to instantiate `Second`, in the constructor, how could that be mocked? – KumarAnkit Jan 10 '19 at 15:03
18

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
}
Rocky Mena
  • 192
  • 1
  • 7
8

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();
 }
distante
  • 6,438
  • 6
  • 48
  • 90
  • 2
    Unclear how this is different from `Foo foo = mock(Foo.class); when(foo.method())...` – OneCricketeer Dec 08 '21 at 21:34
  • The person is trying to mock the call of the constructor, not the return of the method. So, when the constructor of the class is being called, then return an instantiated object the user is providing. – acarlstein Jul 18 '23 at 18:02
5

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

try-catch-finally
  • 7,436
  • 6
  • 46
  • 67
  • I also got Pattern 2 to work however I think there is an important typo in that documentation. See my comment to https://code.google.com/p/mockito/wiki/MockingObjectCreation. – Michael Osofsky Dec 04 '14 at 20:42
  • Awesome i implemented factory pattern and it solved my other question. https://stackoverflow.com/q/47445870/3805770 – KingKongCoder Nov 23 '17 at 02:38
  • 1
    I implemented this pattern. But now, I'm stuck on how to write test case for the Factory class. – v1shnu Jan 19 '21 at 07:49
1

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);
    }
}
JrBenito
  • 973
  • 8
  • 30
  • is that a valid pattern? Sonar will complain of access field `Second` before initialization. If it is not final, that can be circumvent but still, is it a recommended pattern? – JrBenito Mar 25 '20 at 17:56
0

Include this line on top of your test class

@PrepareForTest({ First.class })
Ramesh V
  • 11
  • 2