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

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.

Related

Always getting a empty object in unitTest JPA/Hibernate

I am currently trying if my functions work with my current database but I keep getting an empty object back. My Test is the following:
#Test
public void findJobOfferByIdReturnsCorrectJobOffer() {
User user = UserBuilder.anUser().build();
JobOffer firstJobOffer = JobOfferBuilder.aJobOffer()
.withId(108L)
.withCompany(user)
.build();
JobOffer secondJoboffer = JobOfferBuilder.aJobOffer()
.withAmountPerSession(55)
.withCompany(user)
.withId(208L)
.withJobDescription("Software Tester in PHP")
.build();
userDao.saveUser(user);
jobOfferDao.saveJobOffer(firstJobOffer);
jobOfferDao.saveJobOffer(secondJoboffer);
entityManager.clear();
entityManager.flush();
Optional<JobOffer> retrievedJobOffer = jobOfferDao.findJobOfferById(firstJobOffer.getId());
assertTrue(retrievedJobOffer.isPresent());
JobOffer jobOffer = retrievedJobOffer.get();
assertEquals(jobOffer.getId(), firstJobOffer.getId());
assertNotEquals(jobOffer.getId(), secondJoboffer.getId());
}
The Test uses the following DAOImpl repository:
#Repository
public class JobOfferDaoImpl implements JobOfferDao {
#PersistenceContext
private EntityManager entityManager;
private static final Logger LOGGER = LogManager.getLogger(UserDaoImpl.class);
#Override
public Optional<JobOffer> findJobOfferById(Long id) {
TypedQuery<JobOffer> jobOfferQuery = entityManager.createNamedQuery("findJobOfferById", JobOffer.class);
jobOfferQuery.setParameter("jobOfferId", id);
try {
return Optional.of(jobOfferQuery.getSingleResult());
} catch (NoResultException e) {
return Optional.empty();
}
}
#Transactional
public void saveJobOffer(JobOffer jobOffer) {
if (findJobOfferById(jobOffer.getId()).isEmpty()) {
entityManager.merge(jobOffer);
LOGGER.info(String.format("Joboffer with id %d is inserted in the database", jobOffer.getId()));
} else {
throw new JobOfferNotFoundException();
}
}
}
And the Query to select the correct jobOffer for "findJobOfferById" is the following:
#NamedQuery(name = "findJobOfferById", query = "SELECT j from JobOffer j WHERE j.id = :jobOfferId"),
When trying to debug I get the following:
In the Test you shouldn't give your own ID. Change it with:
.withId(null)
And in the DAO you have to Persist the jobOffer and actually add it. With merge you are modifying it.
entityManager.persist(jobOffer);

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.

Mockito Test Case for Jdbc template and Spring's keyHolder

I wrote a test case but I get a null pointer exception at the return KeyHolder.getKey() line.
My test case looks like this:
#InjectMocks
private UserDAOImpl userDAO;
#Mock
private JdbcTemplate jdbcTemplate;
#Mock
private KeyHolderFactory keyHolderFactory;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
ReflectionTestUtils.setField(userDAO, "jdbcTemplate", jdbcTemplate);
}
#Test
public void testCreateUser() {
KeyHolder keyHolder = mock(GeneratedKeyHolder.class);
when(keyHolderFactory.newKeyHolder()).thenReturn(keyHolder);
User user = getUserInfo();
Map<String,Object> map = new HashMap<>();
map.put("id",1L);
when(keyHolder.getKeys()).thenReturn(map);
when(keyHolder.getKey()).thenReturn(1L);
when(jdbcTemplate.update(Mockito.any(PreparedStatementCreator.class), Mockito.any(KeyHolder.class))).thenReturn(1);
assertEquals(1L, userDAO.createUser(user));
}
And the method looks like this:
#Override
public long createUser(User user) {
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(connection -> {
PreparedStatement ps = connection
.prepareStatement("insert into user (address, first_name,last_name, email, password, phone_number, is_init, is_system_admin, created_at)"
+ " values( ?, ?, ?, ?, ?, ?, ?, ?,?)",
Statement.RETURN_GENERATED_KEYS);
ps.setString(1, user.getAddress());
ps.setString(2, user.getFirstName());
ps.setString(3, user.getLastName());
ps.setString(4, user.getEmail());
ps.setString(5, user.getPassword());
ps.setString(6, user.getPhone());
ps.setBoolean(7, user.isActive());
ps.setBoolean(8, user.isSystemAdmin());
ps.setDate(9, new Date(Date.from((user.getCreatedAt().toInstant())).getTime()));
return ps;
}, keyHolder);
return (long) keyHolder.getKey();
}
I have created an interface KeyHolderFactory:
public interface KeyHolderFactory {
KeyHolder newKeyHolder();
}
And the implementation of this interface is as follows:
public class GeneratedKeyHolderFactory implements KeyHolderFactory {
public KeyHolder newKeyHolder() {
return new GeneratedKeyHolder();
}
}
Can someone help me to find a solution?
Your issue is next :
In test class you have Mocked bin KeyHolderFactory and this is correct. Then you create mocked KeyHolder, describe it's behavior and make KeyHolderFactory return KeyHolder when it is asked. Everything looks OK except next :
On your code class method you create new KeyHolder using new GeneratedKeyHolder() and this new KeyHolder doesn't relate to those mocked instance that you have in your test class. It is absolutely different objects. That one which is in your test class - is a mock, and will follow your scenario. But that one, in your tested class method, is another object, which of course don't know what to do when you execute keyHolder.getKey().
Solution :
You have injected KeyHolderFactory in your test and well configured it's behavior. Just inject this into your test class as well and use it in your method to create KeyHolder using keyHolderFactory.newKeyHolder() instead of new GeneratedKeyHolder()

Jmockit/Spring mocked dependency still calls the Real dependency

so I've been stuck on this problem all day.
I'm testing a class of type JdbcSupportDao in Spring 3.2. The problem is very self-explanatory if you just read the code, but I will briefly summarize:
I use the #Mocked annotation on a JdbcTemplate to mock querying the database. The problem is, after writing the Expectations block, the actual JdbcTemplate method is still being called, with JMockit apparently not entering in to the equation at all.
The following unit test fails:
/*#RunWith(SpringJUnit4ClassRunner.class)*/
#RunWith(JMockit.class)
#ContextConfiguration(locations={"classpath:studentAggregateReport-servlet.xml", "classpath:applicationContext-hibernate.xml"})
public class JdbcSSODaoTest extends AbstractTransactionalJUnit4SpringContextTests {
#Mocked
JdbcTemplate jdbcTemplate;
List<String> unameList;
SSODao ssoDao;
String DUMMY_ALCID = "yattayattayatta";
#Before
public void constructDao() {
this.ssoDao = new JdbcSSODao();
((JdbcSSODao) ssoDao).setJdbcTemplate(jdbcTemplate);
}
#Test
public void testGetUnameFromAlcId() {
unameList = new ArrayList<String>() {{
add("PEEPEE");
}};
//((JdbcSSODao) ssoDao).setJdbcTemplate(jdbcTemplate);
new Expectations() {{
jdbcTemplate.query(anyString, (ResultSetExtractor<String>)any); result = unameList;
}};
String uname = ssoDao.getUnameFromAlcId(DUMMY_ALCID);
assertNotNull(uname);
}
}
and here is the code for the class being tested:
public class JdbcSSODao extends JdbcDaoSupport implements SSODao {
#Override
public String getUnameFromAlcId(String alcid) {
String sql = SSOSqlUtil.createGetUnameByAlcIdSql(alcid);
logger.debug(sql);
List<String> resultLst = getJdbcTemplate().query(sql, new RowMapper<String>() {
public String mapRow(ResultSet rs, int rowNum) throws SQLException {
return rs.getString(1);
}
});
if(resultLst.isEmpty()) return null;
return resultLst.get(0);
}
}
Please help :(
Sweet mother of God..
Apparently, you have to cast parameters of mocked methods to the exact type used in the call. This fixed it for me:
new Expectations() {{
jdbcTemplate.query(anyString, (RowMapper<String>)any); result = unameList;
}};

Spring #Transactional propagation effect of REQUIRES_NEW?

I am doing some tests to understand the behaviour of #Transactional in Spring 3. Though, it is not working as I would expect. If have one method with Propagation.REQUIRED calling another with Propagation.REQUIRES_NEW, will the second method be able to retrieve from the DB the data inserted by the first method?
EDITED:
I AM seeing uncommitted changed in a #Transaction, here is my (nasty looking) code.
#Service
public class FeedManager {
#Autowired
JdbcTemplate jdbcTemplate;
#Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)
public boolean createFeed(Feed feed, boolean anonymizeIt) {
String query = "INSERT INTO feed (name, url, is_active) values (?, ?, ?)";
int rowsAffected = jdbcTemplate.update(query, feed.getName(), feed.getUrl(), feed.isActive());
boolean success = (rowsAffected == 1);
if (anonymizeIt) {
success = success && this.anonymizeFeedName(feed);
}
return success;
}
#Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRES_NEW)
public boolean anonymizeFeedName(Feed feed) {
String query = "UPDATE feed set name = ? where name = ?";
int rowsAffected = jdbcTemplate.update(query, feed.getName() + (new Date()).toString(), feed.getName());
boolean success = (rowsAffected == 1);
return success;
}
}
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("classpath:mrpomario/springcore/jdbc/jdbc-testenv-config.xml")
public class TransactionalTest {
#Autowired
FeedManager feedManager;
Feed feed;
#Before
public void setup() {
feed = new Feed("RSS", "http://www.feedlink.com", true);
}
#Test
public void test_Create() {
assertTrue(feedManager.createFeed(feed, false));
}
#Test
public void test_Anonymize() {
assertTrue(feedManager.anonymizeFeedName(feed));
}
#Test
public void test_Create_And_Anonymize() {
Feed feedo = new Feed("AnotherRSS", "http://www.anotherfeedlink.com", true);
assertTrue(feedManager.createFeed(feedo, true));
}
}
It should not be able to see any changes made by the first method (as long as your isolation level is READ COMMITTED or above).
If you get different results, make sure that #Transactional actually takes effect. In particular, make sure that you don't call another #Transactional method of the same class - due to limitations of Spring proxy-based AOP model transactional aspect is applied only to calls that come from the outside of the class.
See also:
7.6.1 Understanding AOP proxies

Resources