Mock a constructor with parameter

328,184

Solution 1

The code you posted works for me with the latest version of Mockito and Powermockito. Maybe you haven't prepared A? Try this:

A.java

public class A {
     private final String test;

    public A(String test) {
        this.test = test;
    }

    public String check() {
        return "checked " + this.test;
    }
}

MockA.java

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)
@PrepareForTest(A.class)
public class MockA {
    @Test
    public void test_not_mocked() throws Throwable {
        assertThat(new A("random string").check(), equalTo("checked random string"));
    }
    @Test
    public void test_mocked() throws Throwable {
         A a = mock(A.class); 
         when(a.check()).thenReturn("test");
         PowerMockito.whenNew(A.class).withArguments(Mockito.anyString()).thenReturn(a);
         assertThat(new A("random string").check(), equalTo("test"));
    }
}

Both tests should pass with mockito 1.9.0, powermockito 1.4.12 and junit 4.8.2

Solution 2

To my knowledge, you can't mock constructors with mockito, only methods. But according to the wiki on the Mockito google code page there is a way to mock the constructor behavior by creating a method in your class which return a new instance of that class. then you can mock out that method. Below is an excerpt directly from the Mockito wiki:

Pattern 1 - using one-line methods for object creation

To use pattern 1 (testing a class called MyClass), you would replace a call like

   Foo foo = new Foo( a, b, c );

with

   Foo foo = makeFoo( a, b, c );

and write a one-line method

   Foo makeFoo( A a, B b, C c ) { 
        return new Foo( a, b, c );
   }

It's important that you don't include any logic in the method; just the one line that creates the object. The reason for this is that the method itself is never going to be unit tested.

When you come to test the class, the object that you test will actually be a Mockito spy, with this method overridden, to return a mock. What you're testing is therefore not the class itself, but a very slightly modified version of it.

Your test class might contain members like

  @Mock private Foo mockFoo;
  private MyClass toTest = spy(new MyClass());

Lastly, inside your test method you mock out the call to makeFoo with a line like

  doReturn( mockFoo )
      .when( toTest )
      .makeFoo( any( A.class ), any( B.class ), any( C.class ));

You can use matchers that are more specific than any() if you want to check the arguments that are passed to the constructor.

If you're just wanting to return a mocked object of your class I think this should work for you. In any case you can read more about mocking object creation here:

http://code.google.com/p/mockito/wiki/MockingObjectCreation

Solution 3

With Mockito you can use withSettings(). For example if the CounterService required 2 dependencies, you can pass them as a mock:

 UserService userService = Mockito.mock(UserService.class);
 SearchService searchService = Mockito.mock(SearchService.class);   
 CounterService counterService = Mockito.mock(CounterService.class, withSettings().useConstructor(userService, searchService));

Solution 4

Without Using Powermock .... See the example below based on Ben Glasser answer since it took me some time to figure it out ..hope that saves some times ...

Original Class :

public class AClazz {

    public void updateObject(CClazz cClazzObj) {
        log.debug("Bundler set.");
        cClazzObj.setBundler(new BClazz(cClazzObj, 10));
    } 
}

Modified Class :

@Slf4j
public class AClazz {

    public void updateObject(CClazz cClazzObj) {
        log.debug("Bundler set.");
        cClazzObj.setBundler(getBObject(cClazzObj, 10));
    }

    protected BClazz getBObject(CClazz cClazzObj, int i) {
        return new BClazz(cClazzObj, 10);
    }
 }

Test Class

public class AClazzTest {

    @InjectMocks
    @Spy
    private AClazz aClazzObj;

    @Mock
    private CClazz cClazzObj;

    @Mock
    private BClazz bClassObj;

    @Before
    public void setUp() throws Exception {
        Mockito.doReturn(bClassObj)
               .when(aClazzObj)
               .getBObject(Mockito.eq(cClazzObj), Mockito.anyInt());
    }

    @Test
    public void testConfigStrategy() {
        aClazzObj.updateObject(cClazzObj);

        Mockito.verify(cClazzObj, Mockito.times(1)).setBundler(bClassObj);
    }
}

Solution 5

Starting with version 3.5.0 of Mockito and using the InlineMockMaker, you can now mock object constructions:

 try (MockedConstruction mocked = mockConstruction(A.class)) {
   A a = new A();
   when(a.check()).thenReturn("bar");
 }

Inside the try-with-resources construct all object constructions are returning a mock.

Share:
328,184

Related videos on Youtube

Shengjie
Author by

Shengjie

Enthusiastic software engineer, a big fan of open source community development, big data, cloud technologies etc...

Updated on May 28, 2021

Comments

  • Shengjie
    Shengjie almost 3 years

    I have a class as below:

    public class A {
        public A(String test) {
            bla bla bla
        }
    
        public String check() {
            bla bla bla
        }
    }
    

    The logic in the constructor A(String test) and check() are the things I am trying to mock. I want any calls like: new A($$$any string$$$).check() returns a dummy string "test".

    I tried:

     A a = mock(A.class); 
     when(a.check()).thenReturn("test");
    
     String test = a.check(); // to this point, everything works. test shows as "tests"
    
     whenNew(A.class).withArguments(Matchers.anyString()).thenReturn(rk);
     // also tried:
     //whenNew(A.class).withParameterTypes(String.class).withArguments(Matchers.anyString()).thenReturn(rk);
    
     new A("random string").check();  // this doesn't work
    

    But it doesn't seem to be working. new A($$$any string$$$).check() is still going through the constructor logic instead of fetch the mocked object of A.

    • Ben Glasser
      Ben Glasser over 11 years
      is your mocked check() method working right?
    • Shengjie
      Shengjie over 11 years
      @BenGlasser check() works ok. Just the whenNew doesn't seem working at all. I updated the description as well.
  • Shengjie
    Shengjie over 11 years
    +1, I don't like the fact that I need to adjust my source code make it more mockito friendly. Thanks for the sharing.
  • Dawood ibn Kareem
    Dawood ibn Kareem over 11 years
    It's never bad to have source code that is more testable, or to avoid testability anti-patterns when you write your code. If you write source that is more testable, it's automatically more maintainable. Isolating your constructor calls in their own methods is just one way to achieve this.
  • Jeff E
    Jeff E over 9 years
    Also note that if the constructor is called from another class, include it in the list in PrepareForTest
  • udayanga
    udayanga about 6 years
    Anyone has an idea why should we prepare the self when "PowerMockito.whenNew" is called?
  • Admin
    Admin over 3 years
    In my opinion, the easiest and best answer. Thank you.
  • Mark Wood
    Mark Wood over 3 years
    Writing testable code is good. Being forced to redesign class A so that I can write tests for class B, which depends on A, because A has a hard-coded dependency on C, feels...less good. Yeah, the code will be better in the end, but how many classes will I end up redesigning so that I can finish writing one test?
  • Ben Glasser
    Ben Glasser over 3 years
    @MarkWood in my experience clunky testing experiences are generally a sign of some design flaw. IRL if you are testing constructors your code is probably screaming at you for a factory or some dependency injection. If you follow typical design patterns for those two cases your code becomes much easier to test and to work with in general. If you are testing constructors because you have bunch of logic in there, you probably need some layer of polymorphism, or you could move that logic to an initialization method.
  • Nayan
    Nayan almost 3 years
    If there are a lot of parameters, this will be messy.
  • Gowtham Kesa
    Gowtham Kesa over 2 years
    What can be done in the cases where getBObject is private instead of being protected
  • MevlütÖzdemir
    MevlütÖzdemir over 2 years
    @Nayan I Agree. But if you have a lot of parameters/dependencies in your classes, then it looks like something else is wrong.
  • OneCricketeer
    OneCricketeer over 2 years
    How is this different from a field with @InjectMocks?
  • OneCricketeer
    OneCricketeer over 2 years
    How does this differ from A a = mock(A.class)? Also, I don't think this answered the question about parameters
  • Musa
    Musa almost 2 years
    Any junit5 compitable mockito-inline solution?