问题
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