Home > other >  Mock returned value of ResultSet inside the method to be tested that implements an interface
Mock returned value of ResultSet inside the method to be tested that implements an interface

Time:09-05

I'm trying to mock some data with Mockito for some tests, but I can't get it to work. My problem is that the value I want to mock is the return of a call inside the method I need to test.

I have a class, MyAccessor.java (that implements a service, MyService.java) that inside has a method, getMyMethod(String value), and inside that method there is a call to the DB:

final List<MyType> results = this.jdbcTemplate.query(sqlQuery, new RowMapper<MyType>() {
    @Override
    public MyType mapRow(final ResultSet rs, final int rowNum) throws SQLException {
        final int fieldA = rs.getInt("column_name_1");
        final int fieldB = rs.getInt("column_name_2");
        final int fieldC = rs.getInt("column_name_3");
        final int fieldD = rs.getInt("column_name_4");
        return new MyType(fieldA , fieldB , fieldC , fieldD);
    }
}, value);

There is a case where due to timeouts with the database, an empty list is returned by the rowMapper. So in that case it is not possible to do return results.get(0) because you will get an ArrayIndexOutOfBoundsException.

So I have a check:

if (!results.isEmpty()) {
    return results.get(0);
} else {
    return new MyType(0, 0, 0, 0);
}

Now, I want to test this particular scenario but I don't know how to mock the resultSet return so that the rowMapper returns an empty list. In my test class I have set:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { ".....", "....."})
@TestPropertySource(".....")
public class MyClassTest {

    @Mock
    private JdbcTemplate jdbcTemplate;

    @InjectMocks
    @Autowired
    private MyService myService;

    @Before
    public void initMocks() {
        MockitoAnnotations.openMocks(this);
    }

    @Test
    public void myTestMethod() {
        Mockito.when(this.jdbcTemplate.query("SELECT NULL LIMIT 0", new RowMapper<Object>() {
            @Override
            public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
                return new ArrayList<>();
            }
        })).thenReturn(new ArrayList<Object>());

        MyType result = this.myService.getMyMethod("value");
        assertEquals(0, result.getFieldA());
        assertEquals(0, result.getFieldB());
        assertEquals(0, result.getFieldC());
        assertEquals(0, result.getFieldD());
    }
}

But the test fails because the result returned is from the database instead of an empty list to test the timeout case.

I think the problem may be because I'm injecting the mock in the service interface instead of the implementation, but I wanted to know if it can be done somehow so I don't have to test the mock for each implementation of that interface.

Or maybe I am using the Mockito framework wrong and in that case, how can I test correctly?

Regads.

CodePudding user response:

I think you want something more like:

@Test
public void myTestMethod() {
    Mockito.when(this.jdbcTemplate.query(eq("SELECT NULL LIMIT 0"), Mockito.any(RowMapper.class))
           .thenReturn(new ArrayList<Object>());
}

Reason is because when mocking, you were saying only to return the empty list if both arguments to the query is exactly as you defined. In your case, you don't care about the exact instance of RowMapper.

CodePudding user response:

I have found the problem. I was injecting the mock in the interface and not in the implementation of the interface. So I had to change:

@Mock
private JdbcTemplate jdbcTemplate;

@InjectMocks
@Autowired
private MyService myService;

to:

@Mock
private JdbcTemplate jdbcTemplate;

@InjectMocks
@Autowired
private MyAccessor myAccessor;

@Autowired
private MyService myService;

And inside the test this:

MyType result = this.myService.getMyMethod("value");

to:

MyType result = this.myAccessor.getMyMethod("value");

I don't know if there is a better way to do this, without having to instantiate all the implementations that the service may have.

Regards.

  • Related