How to Write the test case for DAO class with JUNIT5 and mockito in spring boot for the code - spring-boot

How to Write the test case for DAO class with JUnit5 and Mockito in spring boot for the code
I am trying to write a test case with JUnit5 and Mockito. How to write the test cases for the below method
DAO class:
public Map<String, Object> addParticipantSubRole(ProductLineParticipantSubRoleDTO subRole, int userId, int buId,int plId) throws RTDataBaseException {
Map<String, Object> returnMap = new HashMap<>();
try {
StoredProcedureQuery query = this.getSession()
.createStoredProcedureCall("PKG_QA_PRODUCT_LINE_ADMIN.PROC_ADD_PARTICIP_SUB_ROLES")
.registerStoredProcedureParameter(1, Integer.class, ParameterMode.IN).setParameter(1, userId)
.registerStoredProcedureParameter(2, Integer.class, ParameterMode.IN).setParameter(2, plId)
.registerStoredProcedureParameter(3, String.class, ParameterMode.IN)
.setParameter(3, subRole.getParticipantSubRoleName())
.registerStoredProcedureParameter(4, String.class, ParameterMode.IN)
.setParameter(4, subRole.getParticipantSubRoleDesc())
.registerStoredProcedureParameter(5, Integer.class, ParameterMode.OUT)
.registerStoredProcedureParameter(6, Integer.class, ParameterMode.OUT)
.registerStoredProcedureParameter(7, String.class, ParameterMode.OUT)
.registerStoredProcedureParameter(8, String.class, ParameterMode.OUT);
int plParticipantSubRoleId = (Integer) query.getOutputParameterValue(5);
int returnId = (Integer) query.getOutputParameterValue(6);
String message = (String) query.getOutputParameterValue(7);
if (returnId == Constants.SUCCESS_INTEGER_VALUE) {
returnMap.put(RESULT, plParticipantSubRoleId);
returnMap.put(RETURN_ID, returnId);
returnMap.put(RETURN_MESSAGE, message);
} else {
returnMap.put(RESULT, subRole);
returnMap.put(RETURN_ID, returnId);
returnMap.put(RETURN_MESSAGE, message);
}
} catch (Exception exception) {
log.error(exception.toString());
throw new RTDataBaseException(userId, "Failed to add PL Sub Role in DB", userId, exception);
}
return returnMap;
}

It makes close to zero sense to do unit testing on the persistance layer. You get a lot of code with close to zero benefit.
Its better to go for an integration test which can look like this ...
#DataJpaTest
class MyDaoTest {
#Autowired private MyDao myDao;
#Test
void testMyDao(){
// Do test setup
myDay.addParticipantSubRole(/* input parameters */)
// Do asserts
}
}

Related

Spring Integration Test Binder doesn't mock dependency

I have a project (https://github.com/serjteplov/demo-kafka-mock.git) where there are couple of dummy functions to read messages from kafka. In one of these functions crudRepository is used to perform some operations against DB:
#EnableAutoConfiguration
public class SampleFunctionConfiguration {
#Bean
public Function<String, String> uppercase(UserRepository userRepository) {
String id = userRepository.findById("abc").map(User::getId).orElse(null);
return value -> value.toUpperCase() + " id=" + id;
}
#Bean
public Function<String, String> reverse() {
return value -> new StringBuilder(value).reverse().toString();
}
}
So the problem is to write test on uppercase binder function. To make this test works correctly I have to mock such call
userRepository.findById("abc")
then I've created mock and added it to the context:
#Primary
#Bean("test")
UserRepository userRepository() {
return new DetachedMockFactory().Mock(UserRepository)
}
and mock call in the test:
userRepository.findById("abc") >> Optional.of(new User("abc", "Bob"))
After test execution mock is successfully created, but userRepository.findById("abc") still returns null.
Can anyone tell how to fix this problem? Or any alternative implementations and workarounds would be nice
You are invoking the stubbed method in the bean definition instead of in the function.
#Bean
public Function<String, String> uppercase(UserRepository userRepository) {
String id = userRepository.findById("abc").map(User::getId).orElse(null);
return value -> value.toUpperCase() + " id=" + id;
}
Vs.
#Bean
public Function<String, String> uppercase(UserRepository userRepository) {
return value -> {
String id = userRepository.findById("abc").map(User::getId).orElse(null);
return value.toUpperCase() + " id=" + id;
};
}

How to write Junit tests for this code with 100% code coverage?

Junit tests for Spring Boot application.
Does anyone have any suggestions on how I might achieve this using JUnit and Mockito?
#Autowired
JdbcTemplate jdbcTemplate;
public List<Student> getStudentDetails(String department) {
List<Student> results = new LinkedList<String>();
results = jdbcTemplate.query("SELECT * FROM STUDENT WHERE DEPARTMENT = ?", new PreparedStatementSetter() {
#Override
public void setValues(PreparedStatement preparedStatement) throws SQLException {
preparedStatement.setString(1, department);
preparedStatement.setFetchSize(10);
}
}, new ResultSetExtractor<List<Student>>() {
#Override
public List<Student> extractData(ResultSet rs) throws SQLException {
List<Student> students = new ArrayList<>();
while (rs.next()) {
Student student = new Student<>();
student.setDepartment(rs.getString("NAME"));
student.setName(rs.getString("DEPARTMENT"));
students.add(student);
}
return students;
}
});
return results
}
Code that you have is related to database and in my opinion it should be tested using some database. Usual practice is to use embedded database ( e.g. h2 ). People do it, since using unit tests it's not possible to check if query really works, since you don't actually run it. So I would combine integration and unit tests for testing of this class.
Unit test would be something like this:
#ExtendWith(MockitoExtension.class)
class StubTest {
#Mock
JdbcTemplate jdbcTemplate;
#InjectMocks
Stub stub;
#Test
void whenExecuteQuery_thenExtractDataCorrectly() throws SQLException {
//GIVEN
ArgumentCaptor<PreparedStatementSetter> setterCaptor = ArgumentCaptor.forClass(PreparedStatementSetter.class);
ArgumentCaptor<ResultSetExtractor> extractorCaptor = ArgumentCaptor.forClass(ResultSetExtractor.class);
//WHEN
stub.getStudentDetails("TEST");
//THEN
verify(jdbcTemplate).query(anyString(), setterCaptor.capture(), extractorCaptor.capture());
//AND
PreparedStatementSetter setter = setterCaptor.getValue();
PreparedStatement preparedStatement = Mockito.mock(PreparedStatement.class);
setter.setValues(preparedStatement);
verify(preparedStatement).setString(1, "TEST");
verify(preparedStatement).setFetchSize(10);
verifyNoMoreInteractions(preparedStatement);
//AND
ResultSetExtractor extractor = extractorCaptor.getValue();
ResultSet rs = Mockito.mock(ResultSet.class);
when(rs.next()).thenReturn(true).thenReturn(false);
when(rs.getString(anyString())).thenReturn("TEST","name");
verifyNoMoreInteractions(rs);
List<Student> students = (List<Student>) extractor.extractData(rs);
assertThat(students.get(0).getName()).isEqualTo("name");
assertThat(students.get(0).getDepartment()).isEqualTo("TEST");
}
}
Here I'm just capturing arguments with business logic that we send to query method. Then I run overridden methods of arguments and verify that they work as we expect.

not able to assert the updated value in tests spring boot jpa

I am trying to perform update with NamedQuery but updated values are not getting persisted in the DB though the update statement returning the updated count. This is happening only in tests but in the actual flow update is happening.
#Test
#Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_UNCOMMITTED)
public void test() throws Exception
messageHandler.process(message);
} catch (Exception e) {
throw new Exception();
}
assertEquals(new Integer(5), ServiceImpl.findById(100L).get().getStatus());
}
Class MessageHandler{
#Transactional
public void process(String message){
serviceImpl.update(5,100, some date, user);
}
}
class ServiceImpl {
#PersistenceContext
EntityManager entityManager;
#Modifying(flushAutomatically = true, clearAutomatically = true)
public void updateOrderStatus(Integer newOrderStatus, Long OrderId, String updateTs,
String updateUserId) {
Query query = entityManager.createNamedQuery(Order.UPDATE_ORDER_STATUS);
entityManager.flush();
query.setParameter(1, newOrderStatus);
query.setParameter(2, OrderId);
query.setParameter(3, updateTs);
query.setParameter(4, updateUserId);
int i = query.executeUpdate();
System.out.println("***************************************");
System.out.println(i);
}
}
Can anyone help me what I am doing wrong in testcases?
Thanks in advance!!!!!!
By default test transactions are rolled back. You'll need to explicitly use #Commit if you want your tests to commit the changes. If you haven't already, take a look at the spring docs.

Spring MVC Test with RestTemplate: Generic collection fails (even with ParameterizedTypeReference)

I am working with Spring Framework 4.3.1
I have the following domain class
#XmlRootElement(name="persona")
#XmlType(propOrder = {"id","nombre","apellido","fecha"})
public class Persona implements Serializable {
#XmlElement(name="id")
#JsonProperty("id")
public String getId() {
return id;
}
....
Where each getter has the #XmlElement and #JsonProperty annotations.
I am working with JAXB2 and Jackson2
I have the following too:
#XmlRootElement(name="collection")
public class GenericCollection<T> {
private Collection<T> collection;
public GenericCollection(){
}
public GenericCollection(Collection<T> collection){
this.collection = collection;
}
#XmlElement(name="item")
#JsonProperty("collection")
public Collection<T> getCollection() {
return collection;
}
public void setCollection(Collection<T> collection) {
this.collection = collection;
}
#Override
public String toString() {
StringBuilder builder = new StringBuilder();
for(Object object : collection){
builder.append("[");
builder.append(object.toString());
builder.append("]");
}
return builder.toString();
}
}
About Testing, the many #Tests methods working through Spring MVC Test work fine. The #Controller and #RestController work how is expected.
Note: I can test the CRUD scenarios, it about the HTTP methods such as POST, PUT, GET and DELETE. Therefore I am able to get one entity and a collection of entities.
Note: from the previous note, all works working around the XML and JSON formats.
Now trying to do testing through the RestTemplate how a kind of programmatic client, it only fails for collections. With the following:
#Before
public void setUp(){
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
restTemplate = new RestTemplate(new MockMvcClientHttpRequestFactory(mockMvc));
List<HttpMessageConverter<?>> converters = new ArrayList<>();
converters.add(httpMessageConverterConfig.marshallingMessageConverter());
converters.add(httpMessageConverterConfig.mappingJackson2HttpMessageConverter());
restTemplate.setMessageConverters(converters);
System.out.println("converters.size():" + converters.size());
}
I can confirm converters.size() always prints 2
The following is for XML and JSON
#Test
public void findAllXmlTest(){
RequestEntity<Void> requestEntity = RestControllerSupport_.createRequestEntityForGet(uri, retrieveURI);
ParameterizedTypeReference<GenericCollection<Persona>> parameterizedTypeReference = new ParameterizedTypeReference<GenericCollection<Persona>>(){};
ResponseEntity<GenericCollection<Persona>> responseEntity = restTemplate.exchange(requestEntity, parameterizedTypeReference);
assertThat(responseEntity, notNullValue());
assertThat(responseEntity.getStatusCode(), is(HttpStatus.OK));
assertThat(responseEntity.getHeaders().getContentType(), is(MediaType.APPLICATION_XML) );
assertThat(responseEntity.getBody(), notNullValue());
assertThat(responseEntity.getBody().getClass(), is(GenericCollection.class));
assertThat(responseEntity.getBody().getCollection(), is(personas));
}
#Test
public void findAllJsonTest(){
RequestEntity<Void> requestEntity = RestControllerSupport_.createRequestEntityForGet(uri, retrieveURI);
ParameterizedTypeReference<GenericCollection<Persona>> parameterizedTypeReference = new ParameterizedTypeReference<GenericCollection<Persona>>(){};
ResponseEntity<GenericCollection<Persona>> responseEntity = restTemplate.exchange(requestEntity, parameterizedTypeReference);
assertThat(responseEntity, notNullValue());
assertThat(responseEntity.getStatusCode(), is(HttpStatus.OK));
assertThat(responseEntity.getHeaders().getContentType(), is(MediaType.APPLICATION_JSON_UTF8) );
assertThat(responseEntity.getBody(), notNullValue());
assertThat(responseEntity.getBody().getClass(), is(GenericCollection.class));
assertThat(responseEntity.getBody().getCollection(), is(personas));
}
Note: observe I am using ParameterizedTypeReference for both scenarios.
For JSON it works.
But for XML I get:
org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [com.manuel.jordan.controller.support.GenericCollection<com.manuel.jordan.domain.Persona>] and content type [application/xml]
at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:109)
What is wrong or missing?
Your problem that you use MarshallingHttpMessageConverter which isn't GenericHttpMessageConverter, like it is expected for the ParameterizedTypeReference in the HttpMessageConverterExtractor:
if (messageConverter instanceof GenericHttpMessageConverter) {
GenericHttpMessageConverter<?> genericMessageConverter =
(GenericHttpMessageConverter<?>) messageConverter;
if (genericMessageConverter.canRead(this.responseType, null, contentType)) {
The MappingJackson2HttpMessageConverter is that one.
So, I suggest you to try with Jaxb2CollectionHttpMessageConverter.

Data not retrieving after saving in database in hibernate 4

I am using Hibernate 4 with Spring 4. I have created my own session factory and used Hibernate Transaction Manager. I have a problem while retrieving the data after saving.
I am saving the data using ProcedureCall and in every method I am opening the session and closing the session. What is the problem? If I remove session.close() then it is working fine.
public Map<String, Object> savePurchaseOrderInvoiceDetail(String dataString, String order_no,String event, HttpSession hs) throws SQLException, ParseException {
HibernateTransactionManager htmLocal = (HibernateTransactionManager) hs.getAttribute("HibernateTransactionManager");
Session session = htmLocal.getSessionFactory().openSession();
Transaction tx = getTransaction(session);
ProcedureCall qry = session.createStoredProcedureCall("purchase_order_invoice_api");
qry.registerParameter(0, String.class, ParameterMode.IN).bindValue(event);
qry.registerParameter(1, String.class, ParameterMode.IN).bindValue(dataString);
qry.registerParameter(2, String.class, ParameterMode.OUT);
qry.registerParameter(3, String.class, ParameterMode.OUT);
qry.registerParameter(4, Integer.class, ParameterMode.OUT);
qry.registerParameter(5, String.class, ParameterMode.OUT);
ProcedureOutputs output = qry.getOutputs();
String msg = (String) output.getOutputParameterValue(2);
String voucheNo=(String) output.getOutputParameterValue(3);
int invoiceId=(int) output.getOutputParameterValue(4);
String status=(String) output.getOutputParameterValue(5);
Map<String, Object>map=new HashMap<String, Object>();
map.put("msg", msg);
map.put("voucherNo", voucheNo);
map.put("lastInvoiceId", invoiceId);
map.put("status", status);
tx.commit();
session.close();
return map;
}
public Map<String, Object> getInvoiceDetails(String invoicedId,HttpSession hs) throws Exception{
HibernateTransactionManager htmLocal = (HibernateTransactionManager) hs.getAttribute("HibernateTransactionManager");
Session session = htmLocal.getSessionFactory().openSession();
final Map<String, Object>map=new HashMap<String, Object>();
String company=(String) hs.getAttribute("company");
int invoiceIdInt=Integer.valueOf(invoicedId);
String qry = "select inv.*,get_supplier_name(inv.Company,inv.Identity) AS CUSTOMER_NAME from invoice_tab inv";
Query query = session.createSQLQuery(qry).addEntity(Invoice.class);
query.setCacheable(false);
List<Invoice> invoiceList = query.list();
for (int i = 0; i < invoiceList.size(); i++) {
Invoice invoiceObj=invoiceList.get(i);
//Business logic
}
session.close();
return map;
}
You are trying hard to to use Spring not to mention the fact that you are having a service (or maybe a repository) dependent on the fact that it is a web application. Both things are bad.
Add the #Transactional annotation to the class containing those methods and enable annotation driven transaction management. Instead of passing around the HttpSession simply inject your dependencies, in this case the SessionFactory.
Don't create sessions yourself use the current session, i.e sessionFactory.getCurrentSession() to obtain a transactional session.
#Service
#Transactional
public class YourService {
private final SessionFactory sessionFactory;
public YourService(SessionFactory sf) {
this.sessionFactory=sf;
}
public Map<String, Object> savePurchaseOrderInvoiceDetail(String dataString, String order_no,String event) throws SQLException, ParseException {
Session session = sessionFactory.getCurrentSession();
ProcedureCall qry = session.createStoredProcedureCall("purchase_order_invoice_api");
qry.registerParameter(0, String.class, ParameterMode.IN).bindValue(event);
qry.registerParameter(1, String.class, ParameterMode.IN).bindValue(dataString);
qry.registerParameter(2, String.class, ParameterMode.OUT);
qry.registerParameter(3, String.class, ParameterMode.OUT);
qry.registerParameter(4, Integer.class, ParameterMode.OUT);
qry.registerParameter(5, String.class, ParameterMode.OUT);
ProcedureOutputs output = qry.getOutputs();
String msg = (String) output.getOutputParameterValue(2);
String voucheNo=(String) output.getOutputParameterValue(3);
int invoiceId=(int) output.getOutputParameterValue(4);
String status=(String) output.getOutputParameterValue(5);
Map<String, Object>map=new HashMap<String, Object>();
map.put("msg", msg);
map.put("voucherNo", voucheNo);
map.put("lastInvoiceId", invoiceId);
map.put("status", status);
return map;
}
public Map<String, Object> getInvoiceDetails(int invoicedId, String company) throws Exception{
Session session = sessionFactory.getCurrentSession();
final Map<String, Object>map=new HashMap<String, Object>();
String qry = "select inv.*,get_supplier_name(inv.Company,inv.Identity) AS CUSTOMER_NAME from invoice_tab inv";
Query query = session.createSQLQuery(qry).addEntity(Invoice.class);
query.setCacheable(false);
List<Invoice> invoiceList = query.list();
for (int i = 0; i < invoiceList.size(); i++) {
Invoice invoiceObj=invoiceList.get(i);
//Business logic
}
return map;
}
}
Something like the above.
What I don't really get is why you are even using hibernate as you are creating your own queries and don't use HQL or anything to get entities. The only thing you use hibernate for is mapping and that can be done with plain SQL also, adding hibernate to your project just for the mapping of your sql results is bit overkill in my book.

Resources