Chry007
Chry007

Reputation: 541

Mockito Allow different argument types to mock overloaded method

For JUnit testing I want to mock an overloaded method. There is no need to implement several methods in the mockbuilder though. I want to do something like this:

Mockito.when(mock.getSomeInfo(Mockito.any(ArgumentType1.class) OR Mockito.any(ArgumentType2.class), Mockito.any(ArgumentType3.class))).then(new Answer<AnswerType>() {..}

I know it doesn't work with the OR statement, but is there another way to do this in Mockito?

Upvotes: 13

Views: 26986

Answers (4)

dting
dting

Reputation: 39287

You can do this with a custom matcher.

Warning: Be reasonable with using complicated argument matching, especially custom argument matchers, as it can make the test less readable. Sometimes it's better to implement equals() for arguments that are passed to mocks (Mockito naturally uses equals() for argument matching). This can make the test cleaner.


public class TypeOrMatcher extends ArgumentMatcher<Object> {

  private final List<Class<?>> clazzes;

  public TypeOrMatcher(Class<?>...clazzes) {
    this.clazzes = new ArrayList<Class<?>>(clazzes);
  }

  public boolean matches(Object actual) {
    if (actual == null) {
      return false;
    }
    Class<?> actualClass = actual.getClass();
    for (Class<?> clazz : clazzes) {
      if (clazz.isAssignableFrom(actualClass) {
        return true;
      }
    }
    return false;
  }
}

TypeOrMatcher isTypeOneOrTwo = new TypeOrMatcher(
    ArgumentType1.class, ArgumentType2.class); 

Some mockObj = mock(Some.class);
when(mockObj.someMethod(argThat(isTypeOneOrTwo), any(ArgumentType3.class))
    .thenReturn(true);

Upvotes: 2

Nayeem
Nayeem

Reputation: 701

As mentioned on the mockito github:

It looks like this has to do with calling when the second time. I couldn't find what was going on, but the second when doesn't attempt to add the mock logic, it just calls the method. Replacing with doReturn().when() works.

    doReturn(expected1).when(overloadedMethods).getString(any());
    doReturn(expected2).when(overloadedMethods).getString(any(), any());

https://github.com/mockito/mockito/issues/1496#issuecomment-423310950 DavidTanner

Upvotes: 3

David Doan
David Doan

Reputation: 129

For an example I have a service class that will be called from testing method:

public interface AService{

   public ReturnObject addNewItem(String param1, String param2);

   public ReturnObject addNewItem(String param1, String param2, boolean isOk);
}

Have a method in MainServiceImpl class will call an overloaded method like below:

@Service("mainService")
public class MainServiceImpl implements MainService {

    @Autowired
    private AService aService;

    public ReturnObject saveItem(String itemName, String itemCode){
        return  aService.addNewItem(itemName, itemCode);
    }
}

So when we have to write unit test for saveItem the method which already calls to overloaded method as addNewItem, if you had used normal way to create a mock then you answer will not return what you want in answer object return.

@RunWith(PowerMockRunner.class)
@PrepareForTest({ })
public class ItemTest{

    @Test
    public void testSaveItem() throws Exception {
       //create a real object of MainServiceImpl 
       MainServiceImpl  mainService = new MainServiceImpl();
       //create a normal way for a mocking object
       AService aService = Mockito.mock(AService.class);
       // Add mock object to MainServiceImpl instance
       ReflectionTestUtils.setField(mainService, "aService", aService);

       //Mock when aService call to addNewItem() method
       PowerMockito.when(aService , "addNewItem", Mockito.anyString(), Mockito.anyString()).then(new Answer<ReturnObject>() {
            @Override
            public ReturnObject answer(InvocationOnMock invocation) throws Throwable {
                return new ReturnObject("saveOK");
            }
        });

        ReturnObject returnObj = mainService.saveItem("Book", "Code123");

        Assert.assertNotNull(returnObj);
    }
}

Try to replace testSaveItem above by with testSaveItem below then success:

@Test
public void testSaveItem() throws Exception {
    //create a real object of MainServiceImpl 
    MainServiceImpl  mainService = new MainServiceImpl();

    //create a special way for a mocking object by add 
    //the answer at create the mock object

    final Answer<ReturnObject> answer = new Answer<ReturnObject>() {
        @Override
        public ReturnObjectanswer(final InvocationOnMock invocation)    throws Throwable {
            return new ReturnObject("saveOK");
        }
    };
    AService aService = Mockito.mock(AService.class, answer);

    // Add mock object to MainServiceImpl instance
    ReflectionTestUtils.setField(mainService, "aService", aService);

    //Mock when aService call to addNewItem() method
    PowerMockito.when(aService , "addNewItem", Mockito.anyString(), Mockito.anyString()).then(new Answer<ReturnObject>() {
            @Override
            public ReturnObject answer(InvocationOnMock invocation) throws Throwable {
                return new ReturnObject("saveOK");
            }
        });

    ReturnObject returnObj = mainService.saveItem("Book", "Code123");

    Assert.assertNotNull(returnObj);
}

Upvotes: 0

Tim van der Lippe
Tim van der Lippe

Reputation: 768

You can pre-create the answer object you want to return and then return this answer.

class A {

    public int test(final String s) {
        return 0;
    }

    public int test(final int i) {
        return 0;
    }
}

And in the test method:

public void testAtest() {
    final A a = Mockito.mock(A.class);

    final Answer<Integer> answer = new Answer<Integer>() {

        @Override
        public Integer answer(final InvocationOnMock invocation) throws Throwable {
            return 0;
        }
    };

    Mockito.when(a.test(Matchers.anyInt())).then(answer);
    Mockito.when(a.test(Matchers.anyString())).then(answer);
}

Upvotes: 1

Related Questions