7

I am write UT with Mockito, and I want replace my mock function(which DB select operation)

class DataBaseSelect {
    List<Long> selectDataFromDB(Long key){
         List<Long>  result  = db.select(key);
         return result;
    }
}

with new function (mock db select using map) I wrote in Test class;

class MapSelect {
    List<Long> selectDataFromMap(Long key){
         List<Long> result = map.get(key);
         return result;
    }
}

and I want return the right value with right key input

I try to do this using ArgumentCaptor as below, but it did not work as I want

ArgumentCaptor<Long> argumentCaptor = ArgumentCaptor.forClass(Long.class);
Mockito.when(dataBaseSelect.selectDataFromDB(argumentCaptor.capture())).thenReturn(MapSelect.selectDataFromMap(argumentCaptor.getValue()));
//some thing else here , 

I want when call dataBaseSelect.selectDataFromDB, it will be mock and then return result from MapSelect.selectDataFromMap and argument is passed from dataBaseSelect.selectDataFromDB

LiQIang.Feng
  • 148
  • 3
  • 11

3 Answers3

5

If you need substitute your own implementation of method, you can do something like that:

// create method substitution
Answer<List<Long>> answer = invocation -> mapSelect.selectDataFromMap((Long) invocation.getArguments()[0]);

// Mock method
Mockito.doAnswer(answer).when(dataBaseSelect).selectDataFromDB(anyLong());

Your solution is incorrect, cause captors are intended for method call verification (how many times method was called), example:

// method calling: mockedObject.someMethod(new Person("John"));
//...
ArgumentCaptor<Person> capture = ArgumentCaptor.forClass(Person.class);
verify(mockedObject).someMethod(capture.capture());
Person expected = new Person("John");
assertEquals("John", capture.getValue().getName());

in this example expected that 'someMethod' was called one time and return person "John"

EshtIO
  • 231
  • 4
  • 8
1

Do not use ArgumentCaptor for stubbing - There is warning in Mockito javadoc. Also read this Mockito : thenAnswer Vs thenReturn - thenReturn is intended for mocking constant result and cannot work in dynamic way.

thenAnswer is what you want.

public class SampleTest {
class DataBaseSelect {
    List<Long> selectDataFromDB(Long key){
        throw new UnsupportedOperationException("I dont want to call this");
    }
}

class MapSelect {
    private final Map<Long,List<Long>> mockMap = new HashMap<Long,List<Long>>(){{
        put(1L,Arrays.asList(-1L,1L));
        put(2L,Arrays.asList(-2L,2L));
    }};
    List<Long> selectDataFromMap(Long key){
        return mockMap.get(key);
    }
}

@Test
public void testThenAnswer(){
    DataBaseSelect dataBaseSelect = Mockito.mock(DataBaseSelect.class);
    MapSelect mapSelect = new MapSelect();

    Mockito.when(dataBaseSelect.selectDataFromDB(Mockito.anyLong()))
            .thenAnswer(invocation ->
                    mapSelect.selectDataFromMap((Long)invocation.getArguments()[0])
            );

    TestCase.assertTrue(
            Arrays.asList(-1L,1L).equals(dataBaseSelect.selectDataFromDB(1L))
    );
    TestCase.assertTrue(
            Arrays.asList(-2L,2L).equals(dataBaseSelect.selectDataFromDB(2L))
    );
}
}

In Java-7 and older replace lambda with anonymous implementation of Answer.

Mockito.when(dataBaseSelect.selectDataFromDB(Mockito.anyLong()))
            .thenAnswer(new Answer<List<Long>>() {
                            @Override
                            public List<Long> answer(InvocationOnMock invocation) throws Throwable {
                                return mapSelect.selectDataFromMap((Long) invocation.getArguments()[0]);
                            }
                        }
            );
Bedla
  • 4,789
  • 2
  • 13
  • 27
-1
  • Do you annotate dataBaseSelect object as @Mock
  • Do you do MockitoAnnotations.initMocks(this);

Please elaborate why did not work. It's simply that your code like this: ....

@Mock
DataBaseSelect dataBaseSelect;

@Before
public void before(){
    MockitoAnnotations.initMocks(this);
}
@Test
public void test() {
     ArgumentCaptor<Long> argumentCaptor = ArgumentCaptor.forClass(Long.class);

     //Maybe you can use when statement Matchers.anyLong()
     //Mockito.when(dataBaseSelect.selectDataFromDB(Matchers.anyLong())).thenReturn(MapSelect.selectDataFromMap(argumentCaptor.getValue()));
     Mockito.when(dataBaseSelect.selectDataFromDB(argumentCaptor.capture())).thenReturn(MapSelect.selectDataFromMap(argumentCaptor.getValue()));

     List<Long> result = dataBaseSelect.selectDataFromDB(argumentCaptor.capture());

     Assert.assertTrue(/*Assert statement*/);
}
ramazankul
  • 452
  • 1
  • 4
  • 14