i am writing test cases for repository classes were i am not able to cover some of lines in repository classes. i need to achieve 85% of code coverage and its mandatory in my case,Please suggest me something
My actual method
public Map<String, String> getProductFamily(List<String> itmNms) {
Map<String, String> productFamilyMap=new HashMap<String, String>();
try {
NamedParameterJdbcTemplate namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate);
String sql = "some query";
MapSqlParameterSource namedParameters = new MapSqlParameterSource();
namedParameters.addValue("itmNms", itmNms);
productFamilyMap = namedParameterJdbcTemplate.query(sql, namedParameters, (ResultSet rs) -> {
Map<String, String> productFamily = new HashMap<>();
while (rs.next()) {
productFamily.put(rs.getString("ITEMNAME"), rs.getString("PRODUCTFAMILY"));
}
return productFamily;
});
}catch (Exception e) {
LOGGER.error("Exception in OracleRespository.getProductFamily : {}", e);
}
return productFamilyMap;
}
Test case for above method
#Test
public void getProductFamily() {
List<String> itmNms = new ArrayList<String>();
itmNms.add("A-SPK-NAMED-USER");
oracleRepo.getProductFamily(itmNms);
Map<String, String> mp = new HashMap<String, String>();
Assert.assertNull(mp);
}
By writing above test cases i am able to cover code coverage till line no 6 below lines i am not able to cover due to below statements
productFamilyMap = namedParameterJdbcTemplate.query(sql, namedParameters, (ResultSet rs) ->{}
Can some one suggest how can i achieve code coverage for above method as 100%.
In cases like that, you need to "manually invoke" the code in lambda. This can be performed with Mockito.doAnswer(...) functionality of Mockito framework. The example (suitable for Mockito 2+):
Mockito.doAnswer(invocationOnMock -> {
ResultSet resultSet = Mockito.mock(ResultSet.class);
Mockito.when(resultSet.next()).thenReturn(true).thenReturn(false);
Mockito.when(resultSet.getString("ITEMNAME")).thenReturn(...);
Mockito.when(resultSet.getString("PRODUCTFAMILY")).thenReturn(...);
ResultSetExtractor<Map<String, String>> resultSetExtractor =
invocationOnMock.getArgument(2);
return resultSetExtractor.extractData(resultSet);
}).when(namedParameterJdbcTemplate).query(
Mockito.anyString(),
Mockito.any(MapSqlParameterSource.class),
Mockito.any(ResultSetExtractor.class)
);
Then you can verify productFamilyMap for populated key-value pair.
If you'd still have troubles with it, you can share your code (e.g. via Github) and I'll try to help you with it.
EDIT: Initially, I didn't notice the thing that NamedParameterJdbcTemplate is created manually with new, and it's kinda hard to mock it. In this case, it's better to refactor your production code a bit - you can create NamedParameterJdbcTemplate object as bean (like you probably did with raw JdbcTemplate) and then inject it into your class (and ofc remove the line where you're creating it with new). Then the things become trivial.
#Component
public class OracleRepository {
private static final Logger LOGGER = LoggerFactory.getLogger(OracleRepository.class);
#Autowired
private NamedParameterJdbcTemplate namedParameterJdbcTemplate; //created as bean in configuration class
public Map<String, String> getProductFamily(List<String> itmNms) {
Map<String, String> productFamilyMap=new HashMap<String, String>();
try {
String sql = "some query";
MapSqlParameterSource namedParameters = new MapSqlParameterSource();
namedParameters.addValue("itmNms", itmNms);
productFamilyMap = namedParameterJdbcTemplate.query(sql, namedParameters, (ResultSet rs) -> {
Map<String, String> productFamily = new HashMap<>();
while (rs.next()) {
productFamily.put(rs.getString("ITEMNAME"), rs.getString("PRODUCTFAMILY"));
}
return productFamily;
});
}catch (Exception e) {
LOGGER.error("Exception in OracleRespository.getProductFamily : {}", e);
}
return productFamilyMap;
}
}
The test class remains unchanged:
#RunWith(MockitoJUnitRunner.class)
public class OracleRepositoryTest {
#InjectMocks
private OracleRepository oracleRepo;
#Mock
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
#Test
public void getProductFamily() {
List<String> itmNms = new ArrayList<>();
itmNms.add("A-SPK-NAMED-USER");
Mockito.doAnswer(invocationOnMock ->{
ResultSet resultSet = Mockito.mock(ResultSet.class);
Mockito.when(resultSet.next()).thenReturn(true).thenReturn(false);
Mockito.when(resultSet.getString("ITEMNAME")).thenReturn("A-SPK-NAMED-USER");
Mockito.when(resultSet.getString("PRODUCTFAMILY")).thenReturn("SPKCLD");
ResultSetExtractor<Map<String, String>> resultSetExtractor =
invocationOnMock.getArgument(2);
return resultSetExtractor.extractData(resultSet);
}).when(namedParameterJdbcTemplate).query(
Mockito.anyString(),
Mockito.any(MapSqlParameterSource.class),
Mockito.any(ResultSetExtractor.class)
);
Map<String, String> productFamilyMap = oracleRepo.getProductFamily(itmNms);
Assert.assertEquals("SPKCLD", productFamilyMap.get("A-SPK-NAMED-USER"));
}
}
The above answer is accurate and is working. I tried in my project too.
Let me try to explain how this works.
We are saying when mocked JDBCTemplate query() method is called, then we want to invoke our own lambda expression with some mocking done, like we first create a mocked result set, and mocks some of its getString methods. Next we capture the third argument of mocked invocation which is result set extractor. Now from here we simply return this extractor extract data method with our mocked result set which will now be invoked.
So in essence we are calling the original extract data method with our mocked result set.
Mockito.doAnswer(invocationOnMock -> {
ResultSet resultSet = Mockito.mock(ResultSet.class);
Mockito.when(resultSet.next()).thenReturn(true).thenReturn(false);
Mockito.when(resultSet.getString("ITEMNAME")).thenReturn(...);
Mockito.when(resultSet.getString("PRODUCTFAMILY")).thenReturn(...);
ResultSetExtractor<Map<String, String>> resultSetExtractor =
invocationOnMock.getArgument(2);
return resultSetExtractor.extractData(resultSet);
}).when(namedParameterJdbcTemplate).query(
Mockito.anyString(),
Mockito.any(MapSqlParameterSource.class),
Mockito.any(ResultSetExtractor.class)
);
Related
We are integrating our application with JIRA, to create an issue with attachment.
Method 1:
public JSONObject createIssue() {
IssueRestClient issueClient = restClient.getIssueClient();
MetadataRestClient metadataClient = restClient.getMetadataClient();
Map<String, Long> priorityMap = new HashMap<>();
Map<String, Long> issueMap = new HashMap<>();
Map<String, String> fieldsMap = new HashMap<>();
// To get the list of all types of priorities
metadataClient.getPriorities().claim().forEach(priorityType -> priorityMap.put(priorityType.getName(), priorityType.getId()));
// To get the list of all issue types
metadataClient.getIssueTypes().claim().forEach(issueType -> issueMap.put(issueType.getName(), issueType.getId()));
// To get the list of all fields, which includes the custom fields
metadataClient.getFields().claim().forEach(fieldType -> fieldsMap.put(fieldType.getName(), fieldType.getId()));
// skipped the remaining code
}
Method 2:
public void addAttachmentToIssue(String issueId, InputStream inputStream, String fileNameWithExtension) {
Issue issue = null;
IssueRestClient issueClient = restClient.getIssueClient();
Promise<Issue> promiseIssue = issueClient.getIssue(issueId);
issue = promiseIssue.claim();
issueClient.addAttachment(issue.getAttachmentsUri(), inputStream, fileNameWithExtension);
}
I'm writing unit tests using Mockito.
public class JIRATest{
#InjectMocks
JIRAService mockJiraService;
JiraRestClient mockJiraRestClient;
IssueRestClient mockIssueRestClient;
Promise mockPromise;
Issue mockIssue;
#BeforeEach
public void setup() {
mockJiraService = new JIRAService();
mockJiraRestClient = mock(JiraRestClient.class);
mockIssueRestClient = mock(IssueRestClient.class);
mockJiraService.setRestClient(mockJiraRestClient);
mockPromise = mock(Promise.class);
mockIssue = mock(Issue.class);
}
#Test
public void addAttachmentTest(){
Mockito.when(mockJiraRestClient.getIssueClient()).thenReturn(mockIssueRestClient);
Mockito.when(mockIssueRestClient.getIssue("DCU")).thenReturn(mockPromise);
Mockito.when(mockPromise.claim()).thenReturn(mockIssue);
Mockito.when(mockIssueRestClient.addAttachment(any(), any(), any()));
String webSite = "https://www.google.com";
URL url = new URL(webSite);
InputStream stream = url.openStream();
mockJiraService.addAttachmentToIssue("issueId", stream, "lowes-webpage.html");
}
}
How to mock these below objects, so that i wanted to use it in thenReturn() of Mockito?
1. Promise<Issue>
2. metadataClient.getPriorities() return Promise<Iterable<Priority>>
3. metadataClient.getPriorities().claim() return Iterable<Priority>
4. metadataClient.getIssueTypes() return Promise<Iterable<IssueType>>
5. metadataClient.getPriorities().claim() return Iterable<IssueType>
6. metadataClient.getFields() return Promise<Iterable<Field>>
7. metadataClient.getFields().claim() return Iterable<Field>
I have a method that collects data from multiple factories using CompletableFuture.supplyAsync like below
#RunWith(MockitoJUnitRunner.StrictStubs.class)
public class TestThenApply {
#Mock private Factory1 factory1;
#Mock private Factory2 factory2;
#Mock private Factory3 factory3;
#Test
public void case1() throws Exception {
when(factory1.create()).thenReturn("factory1");
when(factory2.create()).thenReturn("factory2");
when(factory3.create()).thenReturn("factory3");
Map<String, Object> expected = new HashMap<>();
expected.put("factory1", "factory1");
expected.put("factory2", "factory2");
expected.put("factory3", "factory3");
assertEquals(expected, getModel());
}
#Test
public void case2() throws Exception {
when(factory1.create()).thenThrow(new RuntimeException("Test!!"));
when(factory2.create()).thenReturn("factory2");
when(factory3.create()).thenReturn("factory3");
Map<String, Object> expected = new HashMap<>();
expected.put("factory1", null);
expected.put("factory2", "factory2");
expected.put("factory3", "factory3");
assertEquals(expected, getModel());
}
#Test
public void case3() throws Exception {
when(factory1.create()).thenReturn("factory1");
when(factory2.create()).thenThrow(new RuntimeException("Test!!"));
when(factory3.create()).thenReturn("factory3");
Map<String, Object> expected = new HashMap<>();
expected.put("factory1", "factory1");
expected.put("factory2", null);
expected.put("factory3", "factory3");
assertEquals(expected, getModel());
}
#Test
public void case4() throws Exception {
when(factory1.create()).thenReturn("factory1");
when(factory2.create()).thenReturn("factory2");
when(factory3.create()).thenThrow(new RuntimeException("Test!!"));
Map<String, Object> expected = new HashMap<>();
expected.put("factory1", "factory1");
expected.put("factory2", "factory2");
expected.put("factory3", null);
assertEquals(expected, getModel());
}
private Map<String, Object> getModel() {
Map<String, Object> map = new HashMap<>();
CompletableFuture<Void> factory1Future = supplyAsyncWithCallable("factory1", () -> factory1.create())
.thenAccept(result -> map.put("factory1", result));
CompletableFuture<Void> factory2Future = supplyAsyncWithCallable("factory2", () -> factory2.create())
.thenAccept(result -> map.put("factory2", result));
CompletableFuture<Void> factory3Future = supplyAsyncWithCallable("factory3", () -> factory3.create())
.thenAccept(result -> map.put("factory3", result));
CompletableFuture.allOf(factory1Future, factory2Future, factory3Future).join();
return map;
}
private <T> CompletableFuture<T> supplyAsyncWithCallable(String widgetName, Callable<T> widgetModelBuilder) {
return CompletableFuture.supplyAsync(() -> {
try {
return widgetModelBuilder.call();
} catch (Exception e) {
return null;
}
});
}
}
I have 4 unit tests which tests below scenarios
When all 3 factories succeeds // Happy case
When factory1 fails but other 2 factories succeed
When factory2 fails but other 2 factories succeed
When factory3 fails but other 2 factories succeed
90% of the time all these test cases pass however one test case fails (assertion error) randomly sometimes.
I suspect this might be because thenAccept does not run before the main thread ends. I was able to get it working all the time by moving all the thenAccept logic after the join(). However I don't want to do that unless I have to. Any help is appreciated.
Thanks to #ernest_k
Using HashMap was the issue here. Replaced it with thread safe map and now the test cases pass consistently.
I have the following Observable where I am expecting some DB insertions to occur upon subscribing to it.
But nothing happens, no DB inserts and same time no errors either.
But If I directly subscribe to the method that does the DB calls, the DB insert occurs as expected.
How can I fix this such that the subscription to the Observable call below will perform the DB insert?
Please advice. Thanks.
This is the Observable where no DB insert occurs and no errors. I want to change this such that the DB insertion occurs when I subscribe to this Observable.
public Observable<KafkaConsumerRecord<String, RequestObj>> apply(KafkaConsumerRecords<String, RequestObj> records) {
Observable.from(records.getDelegate().records().records("TOPIC_NAME"))
.buffer(2)
.map(this::convertToEventRequest)
.doOnNext(this::handleEventInsertions)
.doOnSubscribe(() -> System.out.println("Subscribed!"))
.subscribe(); // purposely subscribing here itself to test
return null; // even if I return this observable and subscribe at the caller, same outcome.
}
Just to test if the query works, if I were to directly subscribe to the method that does the insertion, it works as expected as follows.
Doing this in debug mode.
client.rxQueryWithParams(query, new JsonArray(params)).subscribe() // works
The following are references to see whats happening inside the convertToEventRequest and handleEventInsertions methods
private Map<String, List<?>> convertToEventRequest(Object records) {
List<ConsumerRecord<String, RequestObj>> consumerRecords = (List<ConsumerRecord<String, RequestObj>>) records;
List<AddEventRequest> addEventRequests = new ArrayList<>();
List<UpdateEventRequest> updateEventRequests = new ArrayList<>();
consumerRecords.forEach(record -> {
String eventType = new String(record.headers().headers("type").iterator().next().value(), StandardCharsets.UTF_8);
if("add".equals(eventType)) {
AddEventRequest request = AddEventRequest.builder()
.count(Integer.parseInt(new String(record.headers().headers("count").iterator().next().value(), StandardCharsets.UTF_8)))
.data(record.value())
.build();
addEventRequests.add(request);
} else {
UpdateEventRequest request = UpdateEventRequest.builder()
.id(new String(record.headers().headers("id").iterator().next().value(), StandardCharsets.UTF_8))
.status(Integer.parseInt(new String(record.headers().headers("status").iterator().next().value(), StandardCharsets.UTF_8)))
.build();
updateEventRequests.add(request);
}
});
return new HashMap<String, List<?>>() {{
put("add", addEventRequests);
put("update", updateEventRequests);
}};
}
private void handleEventInsertions(Object eventObject) {
Map<String, List<?>> eventMap = (Map<String, List<?>>) eventObject;
List<AddEventRequest> addEventRequests = (List<AddEventRequest>) eventMap.get("add");
List<UpdateEventRequest> updateEventRequests = (List<UpdateEventRequest>) eventMap.get("update");
if(addEventRequests != null && !addEventRequests.isEmpty()) {
insertAddEvents(addEventRequests);
}
if(updateEventRequests != null && !updateEventRequests.isEmpty()) {
insertUpdateEvents(updateEventRequests);
}
}
private Single<ResultSet> insertAddEvents(List<AddEventRequest> requests) {
AddEventRequest request = requests.get(0);
List<Object> params = Arrays.asList(request.getCount(), request.getData());
String query = "INSERT INTO mytable(count, data, creat_ts) " +
"VALUES (?, ?, current_timestamp)";
return client.rxQueryWithParams(query, new JsonArray(params));
}
private Single<ResultSet> insertUpdateEvents(List<UpdateEventRequest> requests) {
UpdateEventRequest request = requests.get(0);
return client.rxQueryWithParams(
"UPDATE mytable SET status=?, creat_ts=current_timestamp WHERE id=?",
new JsonArray(Arrays.asList(request.getStatus(), request.getId())));
}
Can you try to wrap it into Observable.defer?
Observable.defer(() -> Observable.from(records.getDelegate().records().records("TOPIC_NAME"))...
In my spring boot application, we have service, controller and model.
The controller has:
#RequestMapping(value = "/v1/abc/def", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ServiceResponse<Map<String, List<ClicksReply>>> getAbcCall(#RequestParam(value = "Id") String Id,
#RequestParam(value = "Tag") List<String> Tag) throws SQLException {
Map<String, List<ClicksReply>> clicks = mysqlService.getReplyCount(pageId, notificationTag);
return new ServiceResponse<>(HttpStatus.OK, clicks);
}
mysqlService.getReplyCount looks like this:
public Map<String, List<ClicksReply>> getReplyCount(String pageId, List<String> notificationTag) {
String notificationIds = getStringForInQuery(notificationTag);
try (PreparedStatement preparedStatement = connection.prepareStatement(String.format(GET_CLICK_COUNT, notificationIds))) {
Map<String, List<Clicks
Reply>> mapNotifsButtonCount = new HashMap<>();
preparedStatement.setString(1, pageId);
ResultSet resultSet = preparedStatement.executeQuery();
while (resultSet.next()) {
ClicksReply reply = new ClicksReply();
Integer buttonId = resultSet.getInt(2);
Integer clickCount = resultSet.getInt(3);
reply.setButtonId(buttonId);
reply.setCount(clickCount);
String tag = resultSet.getString(1);
if (!mapNotifsButtonCount.containsKey(tag)) {
List<ClicksReply> clicksReplies = new LinkedList<>();
mapNotifsButtonCount.put(tag, clicksReplies);
}
List<ClicksReply> existinglist = mapNotifsButtonCount.get(tag);
existinglist.add(reply);
}
resultSet.close();
preparedStatement.close();
return mapNotifsButtonCount;
} catch (SQLException exception) {
return null;
}
}
I am new to Java Stack and I tried writing unit test after following some basics, this is how far I got:
#RunWith(SpringRunner.class)
#WebMvcTest(value = StatsController.class, secure = false)
public class StatsTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private MysqlService mysqlService;
#Test
public void getReplyCount() throws Exception {
Map<String, List<ClicksReply>> mapClicksReply = new HashMap();
Mockito.when(
mysqlService.getQuickReplyCount(
Mockito.any(String.class), Mockito.anyListOf(String.class)
)
).thenReturn(mapClicksQuickReply);
RequestBuilder requestBuilder = MockMvcRequestBuilders.get(
"/v1/abc/def")
.param("Id", Mockito.anyString())
.param("Tag", Mockito.anyString())
.accept(
MediaType.APPLICATION_JSON);
mockMvc.perform(requestBuilder).
andExpect(jsonPath("$.httpStatusCode").value(200))
.andExpect(jsonPath("$.errorMessage").value(IsNull.nullValue()))
.andDo(print());
}
}
What should the next step be to actually "unit test" the business logic, DB connection and query results? What I have done so far is more like high level API test that checks the status.
I am not sure of the direction so as to check the business logic now.
I think there is not a 'right' answer, but I would first split up the getReplyCount method because it is hard to test at the moment. This method does currently multiple things:
prepares the query
fetch the data with the prepared statement
maps the data to the ClicksReply class
groups the items by tags
With the much smaller scope you can much easier test different scenarios like:
valid / invalid input parameters
constructing the query
failing query execution
different issues with the mapping
correct grouping of the items
Also on you API test you can add more scenarios like error, invalid input etc.
What me personally helped in the past was a book about testing / tdd in Java it gave me much more insights what to consider on testing because there are a lot of pitfalls especially on maintaining a good test suite over time.
I hope that helped.
regards, wipu
I have a webapp where I need to retrieve data from DB via JDBC and display it front end. Its a static data and it wont be changed. So I just need the data to be retrieved just once and store in a static variable and so I can use that data every time instead of querying the database. Below is the sample code:
public class SampleClass(){
static Map<String, BigDecimal> productMap = null;
public Map<String, BigDecimal> getDatafromDB(Connection conn, PreparedStatement stmt, ResultSet rs) throws SQLException{
if(productMap == null){
productMap = getProductID(conn, stmt, rs);
}
return productMap;
}
public Map<String, BigDecimal> getProductID(Connection conn, PreparedStatement stmt, ResultSet rs) throws SQLException {
logger.debug("retrieving product ID's from product table..");
Map<String, BigDecimal> productMap = new HashMap<String, BigDecimal>();
stmt = conn.prepareStatement("Select * from PRODUCTTABLE");
rs = stmt.executeQuery();
logger.debug("Product ID's retrieved");
while(rs.next()){
productMap.put(rs.getString("PRODUCT_NAME"),rs.getBigDecimal("PRODUCT_ID"));
}
if (stmt != null) {
stmt.close();
}
if (rs != null) {
rs.close();
}
return productMap;
}
}
I am calling this method from UI through a http servlet request. For the first time when I run it, since the map is null it queries the data base and gets the data and sends it to UI. When I hit the servlet again for the second time with the new request the product map is null and again its querying the database and getting the data. Since its a static variable it should be only initialized once rite so why is it still null for the second request. Can someone please correct my code ?
Thanks in advance
The root cause of your problem is that a servlet is memoryless. So a static variable will not help. What you need is a session attribute instead.
I suggest adding your variable as a session attribute. In the first place you need to create an encapsulating class that implements Serializable:
public class SampleResult implements Serializable {
private Map<String, String> productMap;
public void setProductMap(Map<String, String> productMap) {
this.productMap = productMap;
}
public Map<String, String> getProductMap() {
return productMap;
}
}
Now in your servlet:
HttpSesssion session = request.getSession();
Map<String, String> productMap;
SampleResult result;
if (session.getAttribute("productMap") == null) {
productMap = retrievedatafromDB();
result = new SampleResult();
sampleResult.setProductMap(productMap);
session.setAttribute("productMap", result);// session attribute created
} else {
result = session.getAttribute("productMap");// retrieve session attribute
productMap = result.getProductMap();
}