How to test SimpleJdbcCall

狂风中的少年 提交于 2021-02-05 08:55:18

问题


I need to create test for this code.

@Autowired
JdbcTemplate jdbcTemplate;

public List<Row> getData(int id) {
    // Preconditions here

    SimpleJdbcCall getCall = new SimpleJdbcCall(jdbcTemplate)
            .withSchemaName(SCHEMA)
            .withProcedureName(SP)
            .declareParameters( 
                // ...
            )
            .returningResultSet("result", (RowMapper<QuestionAnswerRow>) (rs, rowNum) -> .....);

    MapSqlParameterSource params = new MapSqlParameterSource();
    params.addValue("id", id);
    // other parameters here

    Map queryRes = getCall.execute(params);
    List<row> res = (List<row>) queryRes.get("result");
    return res;
}

Could you show me how can I mock here getCall.execute(params) response with Mockito?


回答1:


Your problem here is, that you create the whole getCall object inside your method, which makes it pretty much impossible to somehow inject a mock into that process.

There are a few possibilities here:

a) You can mock your jdbcTemplate and then try to get that to work by mocking all calls that getCall will make to the jdbcTemplate. Since the jdbcTemplate will probably do all the actual db work, this can work, but honestly, it's probably not worth the effort (since it's absolutely non-trivial).

b) You can switch the whole test to an integration-test, letting it run against an in-memory db, for example. Of course, there are many arguments against using an integration test as a substitute for an unit test, so probably this isn't the best way to go, either. It's possible, though, and using the Spring test utils and annotations it can be pretty simple.

c) This leaves us with a bit of work, which in this case means refactoring:

Since your problem is that you create the SimpleJdbcCall internally, one solution would be to to extract that part, for example, into a factory. This simplified example shows that:

@Component
class SimpleJdbcCallFactory {
   public SimpleJdbcCall create(JdbcTemplate template) {
       return new SimpleJdbcCall(template);
   }
}

Now you can add an @Autowired dependency to your class and then mock that depencency in your unit test...

@RunWith(MockitoJUnitRunner.class)
public class YourTestClassHere {

   @Mock
   private SimpleJdbcCallFactory simpleJdbcCallFactory;

   @InjectMocks
   private YourClassHere classToTest;

   @Test
   public void test() {
        SimpleJdbcCall mockedCall = Mockito.mock(SimpleJdbcCall.class);
        Mockito.when( simpleJdbcCallFactory.create(Mockito.any())).thenReturn(mockedCall);
        Mockito.when( mockedCall ).withSchemaName(Mockito.anyString()).thenReturn(mockedCall);
        // etc. unfortunately needed for fluent apis (unless they added those in mockito)
        Mockito.when( mockedCall.execute(Mockito.any()).thenReturn( ... );

        classToTest.getData(123);
   }

}



回答2:


To resolve the no datasource specified we need to mock SimpleJdbcCall, the above code is calling original method. so we are getting above error.

Please use power mockito to mock parameterized constructor as shown in below

JdbcTemplate jdbcTemplateMock = Mockito.mock(JdbcTemplate.class);
SimpleJdbcCall mockedSimpleJdbcCall = Mockito.mock(SimpleJdbcCall.class);

PowerMockito.whenNew(SimpleJdbcCall.class).withArguments(jdbcTemplateMock).thenReturn(mockedSimpleJdbcCall);


来源:https://stackoverflow.com/questions/47429826/how-to-test-simplejdbccall

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!