1

I want to implement a database reader using Spring and JDBC.

@Component
public class MyReader() {

    public void read() {
        /* Other code */

        ResultSet rs = stmt.executeQuery(sql);
        while(rs.next()){
            String myDbValue = rs.getString("myColumn");
        }

        /* Other code */
    }
}

I want to test the behavior of my class, if column myColumn isn't present.

One solution would be to move the constant myColumn to a private method, but some guys on SO told other users, that mocking private methods smells, see Raedwalds comment and I would agree. I could also mock the database file itself, but mocking files isn't a good way too.

Any ideas how handle this issue?

Maciej Kowalski
  • 25,605
  • 12
  • 54
  • 63
user3417078
  • 265
  • 4
  • 15
  • i think you should use exception Handling at `rs.getString("myColumn")` to perform the testing.. if any exception raised then throw it at this line and catch at the caller method. @user3417078 – Vikrant Kashyap Oct 18 '16 at 04:44
  • A bigger question is why you want to check `myColumn` does not exist? That aside, you can refactor your code so you pass a `ResultSet` or ideally it's interface as a parameter for your method. This way you can mock the interface and pass to your method during test. – timothyclifford Oct 18 '16 at 04:46

1 Answers1

3

Your test code, and therefore what/how you choose to mock, is intrinsically and inextricably linked to the functionality of the method you're testing. Now, this doesn't mean that it should be tied to the implementation of that method (you want to keep this fairly decoupled), but the core purpose of what the method aims to do is what you should be testing. This is why well-designed, well thought out tests drives good API design.

The question you therefore need to ask yourself is What is the read method doing?.

I suspect that both the Statement and the ResultSet returned from executing the query are implementation details that you can mock out for the purposes of your test.

import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import static org.mockito.Matchers.*;

import java.sql.*;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner;

@RunWith(MockitoJUnitRunner.class)
public class MyReaderTest {

    @InjectMocks
    private MyReader myReader;

    @SuppressWarnings("unchecked")
    @Before
    public void setUp() throws Exception {
        Statement s = mock(Statement.class);
        ResultSet rs = mock(ResultSet.class);

        when(s.executeQuery(anyString())).thenReturn(rs);
        when(rs.getString("myColumn")).thenThrow(SQLException.class);
    }

    @Test
    public void testRead_AccessNonExistentColumn() {
        // Use mock statement and mock resultset
    }

}
nbrooks
  • 18,126
  • 5
  • 54
  • 66
  • in simple terms mock everything you get? –  Oct 18 '16 at 04:46
  • @user6601906 The whole purpose of mocking is to test function behavior, not the behavior of the functions being called within that function. Hence, yes, mock any call outside of this function that talks to a 3rd party system. – Aditya Satyavada Jan 11 '18 at 19:14