Mockito Test Case for Jdbc template and Spring's keyHolder - spring

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()

Related

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.

how to use mockito and junit to test my spring boot rest api?

I'm new in unit testing and cannot figure out how to test the RESTFul API with Spring using Mockito and Junit, first of all i have preapared the class in which i created two method with #Before and #Test, when i turned in debugging mode it tells me that hasSize collection return 0 not 4.
#RunWith(SpringJUnit4ClassRunner.class)
public class EmployeControllerTest {
private MockMvc mockMvc;
#InjectMocks
private EmployeController employeController ;
#Mock
private EmployeService employeService ;
#Before
public void setUp() throws Exception{
MockitoAnnotations.initMocks(this);
mockMvc=MockMvcBuilders.standaloneSetup(employeController).build();
}
#Test
public void testgetAllEmployee() throws Exception{
List<Employe> employes= Arrays.asList(
new Employe("Hamza", "Khadhri", "hamza1007", "123")
,new Employe("Oussema", "smi", "oussama", "1234") );
when(employeService.findAll()).thenReturn(employes);
mockMvc.perform(get("/employe/dto"))
.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
.andExpect(jsonPath("$", hasSize(4)))
.andExpect(jsonPath("$[0].nom", is("Hamza")))
.andExpect(jsonPath("$[0].prenom", is("Khadhri")))
.andExpect(jsonPath("$[0].login", is("hamza1007")))
.andExpect(jsonPath("$[0].mp", is("123")))
.andExpect(jsonPath("$[1].nom", is("Oussema")))
.andExpect(jsonPath("$[1].prenom", is("smi")))
.andExpect(jsonPath("$[1].login", is("oussama")))
.andExpect(jsonPath("$[1].mp", is("1234")));
verify(employeService,times(1)).findAll();
verifyNoMoreInteractions(employeService);
}
}
here is the EmployeController :
#CrossOrigin(origins = "*", allowedHeaders = "*")
#RestController
#RequestMapping("/employe")
public class EmployeController {
#Autowired
private EmployeService employeService;
#Autowired
private ModelMapper modelMapper;
#GetMapping("/dto")
public List<EmployeDTO> getEmployeDTOList(){
try {
List<Employe> listemp=employeService.findAllEmployeActive();
return listemp.stream()
.map(emp ->convertToDto(emp))
.collect(Collectors.toList());
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private EmployeDTO convertToDto(Employe emp) {
EmployeDTO empDto = modelMapper.map(emp, EmployeDTO.class);
return empDto;
}
}
when I turn in debugging mode, it tells me that there is NullPointerException.
How to use properly mockito and junit to succeed the test?
I see a few potential issues:
First, you are calling
List<Employe> listemp=employeService.findAllEmployeActive();
in your controller's getEmployeDTOList(), but your Mockito mock is written as:
when(employeService.findAll()).thenReturn(employes)
So, your test might not be working simply because your mock never happens.
Second, you have not mocked the ModelMapper that you autowired in your controller. I think Spring will still go ahead and grab that component for you (someone correct me if I'm wrong there), but either way it's not great practice to have your unit tests be dependent on external libraries since you should only be concerned with the controller's functionality. It would be better to mock ModelMapper to make it "always" work and write separate tests to validate your mappings.
I went ahead and made my own version of your code to test things out. I changed hasSize to 2 because you were only using two elements in your example.
This test is working for me:
#RunWith(SpringJUnit4ClassRunner.class)
public class EmployeeControllerTest {
private MockMvc mockMvc;
#InjectMocks
private EmployeeController employeeController ;
#Mock
private EmployeeService employeeService;
#Mock
private ModelMapper modelMapper;
#Before
public void setUp() throws Exception{
MockitoAnnotations.initMocks(this);
mockMvc=MockMvcBuilders.standaloneSetup(employeeController).build();
}
#Test
public void testgetAllEmployeeWithModelMapper() throws Exception{
Employee emp1 = new Employee("Hamza", "Khadhri", "hamza1007", "123");
Employee emp2 = new Employee("Oussema", "smi", "oussama", "1234");
List<Employee> Employees= Arrays.asList(emp1, emp2);
EmployeeDTO dto1 = new EmployeeDTO("Hamza", "Khadhri", "hamza1007", "123");
EmployeeDTO dto2 = new EmployeeDTO("Oussema", "smi", "oussama", "1234");
when(modelMapper.map(emp1,EmployeeDTO.class)).thenReturn(dto1);
when(modelMapper.map(emp2,EmployeeDTO.class)).thenReturn(dto2);
when(employeeService.findAll()).thenReturn(Employees);
mockMvc.perform(get("/employe/dto"))
.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
.andExpect(jsonPath("$", hasSize(2)))
.andExpect(jsonPath("$[0].nom", is("Hamza")))
.andExpect(jsonPath("$[0].prenom", is("Khadhri")))
.andExpect(jsonPath("$[0].login", is("hamza1007")))
.andExpect(jsonPath("$[0].mp", is("123")))
.andExpect(jsonPath("$[1].nom", is("Oussema")))
.andExpect(jsonPath("$[1].prenom", is("smi")))
.andExpect(jsonPath("$[1].login", is("oussama")))
.andExpect(jsonPath("$[1].mp", is("1234")));
verify(employeeService,times(1)).findAll();
verifyNoMoreInteractions(employeeService);
}
}
As you can see I create my own DTO objects and pass them back so that modelMapper will always behave as expected for this unit test.
This is the code that works but it ignores the object emp1:
public void testgetAllEmployeeWithModelMapper() throws Exception{
Employe emp1 = new Employe("Hamza", "Khadhri", "hamza1007", "123");
Employe emp2 = new Employe("Oussem", "smi", "oussama", "1234");
List<Employe> Employees= Arrays.asList(emp1, emp2);
EmployeDTO dto1 = new EmployeDTO("Hamza", "Khadhri", "hamza1007", "123");
EmployeDTO dto2 = new EmployeDTO("Oussem", "smi", "oussama", "1234");
when(modelMapper.map(emp1,EmployeDTO.class)).thenReturn(dto1);
when(modelMapper.map(emp2,EmployeDTO.class)).thenReturn(dto2);
when(employeeService.findAll()).thenReturn(Employees);
mockMvc.perform(get("/employe/dto"))
.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
.andExpect(jsonPath("$", hasSize(2)))
.andExpect(jsonPath("$[0].nom", is("Hamza")))
.andExpect(jsonPath("$[0].prenom", is("Khadhri")))
.andExpect(jsonPath("$[0].login", is("Hamza1007")))
.andExpect(jsonPath("$[0].mp", is("123")))
.andExpect(jsonPath("$[1].nom", is("Oussema")))
.andExpect(jsonPath("$[1].prenom", is("smi")))
.andExpect(jsonPath("$[1].login", is("oussama")))
.andExpect(jsonPath("$[1].mp", is("1234")));
verify(employeeService,times(1)).findAll();
verifyNoMoreInteractions(employeeService);
}
}
The cosole shows me that jsonPath("$[0].nom" expect Oussema
so when i change it to Oussema which is the object emp2 and it works well. unfortunately it ignores the the object emp1 which contain Hamza,
personnaly i just add two constructor in Employe and EmployeDTO.
this is the class Employe:
public class Employe implements Serializable {
#Id
#Column(unique=true, nullable=false, precision=6)
private Long id;
#Column(precision=6)
private BigDecimal cv;
#Column(length=254)
private String nom;
#Column(length=254)
private String prenom;
#Column(length=254)
private String login;
#Column(length=254)
private String mp;
#Column(length=254)
private String mail;
#Column(precision=6)
private BigDecimal idpointage;
#Column(length=1)
private Boolean actif;
public Employe(String nom, String prenom, String login, String mp) {
this.nom = nom;
this.prenom = prenom;
this.login = login;
this.mp = mp;
}
this is the class EmployeDTO:
public class EmployeDTO {
private String nom;
private String prenom;
private String login ;
private String mp ;
public EmployeDTO(String nom, String prenom, String login, String mp) {
this.nom= nom ;
this.prenom= prenom ;
this.login = login ;
this.mp=mp ;
}

MockMvc returns null instead of object

I am developing a microservice application and I need to test a post request
to a controller. Testing manually works but the test case always returns null.
I've read many similar questions here in Stackoverflow and documentation but haven't figured out yet what I am missing.
Here is what I currently have and what I tried in order to make it work:
//Profile controller method need to be tested
#RequestMapping(path = "/", method = RequestMethod.POST)
public ResponseEntity<Profile> createProfile(#Valid #RequestBody User user, UriComponentsBuilder ucBuilder) {
Profile createdProfile = profileService.create(user); // line that returns null in the test
if (createdProfile == null) {
System.out.println("Profile already exist");
return new ResponseEntity<>(HttpStatus.CONFLICT);
}
HttpHeaders headers = new HttpHeaders();
headers.setLocation(ucBuilder.path("/{name}").buildAndExpand(createdProfile.getName()).toUri());
return new ResponseEntity<>(createdProfile , headers, HttpStatus.CREATED);
}
//ProfileService create function that returns null in the test case
public Profile create(User user) {
Profile existing = repository.findByName(user.getUsername());
Assert.isNull(existing, "profile already exists: " + user.getUsername());
authClient.createUser(user); //Feign client request
Profile profile = new Profile();
profile.setName(user.getUsername());
repository.save(profile);
return profile;
}
// The test case
#RunWith(SpringRunner.class)
#SpringBootTest(classes = ProfileApplication.class)
#WebAppConfiguration
public class ProfileControllerTest {
#InjectMocks
private ProfileController profileController;
#Mock
private ProfileService profileService;
private MockMvc mockMvc;
private static final ObjectMapper mapper = new ObjectMapper();
private MediaType contentType = MediaType.APPLICATION_JSON;
#Before
public void setup() {
initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(profileController).build();
}
#Test
public void shouldCreateNewProfile() throws Exception {
final User user = new User();
user.setUsername("testuser");
user.setPassword("password");
String userJson = mapper.writeValueAsString(user);
mockMvc.perform(post("/").contentType(contentType).content(userJson))
.andExpect(jsonPath("$.username").value(user.getUsername()))
.andExpect(status().isCreated());
}
}
Tried to add when/thenReturn before post but still returns 409 response with null object.
when(profileService.create(user)).thenReturn(profile);
You're using a mock profileService in your test, and you never tell that mock what to return. So it returns null.
You need something like
when(profileService.create(any(User.class)).thenReturn(new Profile(...));
Note that using
when(profileService.create(user).thenReturn(new Profile(...));
will only work if you properly override equals() (and hashCode()) in the User class, because the actual User instance that the controller receives is a serialized/deserialized copy of the user you have in your test, and not the same instance.

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

How to write a search class to accept any type of parameter?

I'm using spring mvc and I created the CRUD functionality. But I want to create a search function that will allow me to find a user by any parameter (variable) as 'userid' or 'username' or 'lastname' or 'social security number' or whatever.
My userid is an integer type.
How can I do that? What is the SQL query for that?
How can I check if the input is integer or string and then go through the database by the given parameter and search for the user?
If you are using Hibernate for data access you can easily create universal finder using criteria API:
Abstract DAO class:
public abstract class AbstractHibernateDAO<T> {
private static final String PARAM_VALUE_PARAMETER = "paramValue";
private final Class<T> clazz;
#Autowired
private SessionFactory sessionFactory;
public AbstractHibernateDAO(Class<T> clazz) {
this.clazz = clazz;
}
public T findOne(String paramName, Object paramValue) {
Session session = sessionFactory.getCurrentSession();
#SuppressWarnings("unchecked")
T fetchedObject = (T) session.createCriteria(clazz).add(Restrictions.eq(paramName, paramValue)).uniqueResult();
return fetchedObject;
}
// Other CRUD methods.
}
Concrete DAO class for entity:
#Repository
#Transactional
public class ProductHibernateDAO extends AbstractHibernateDAO<Product> {
public ProductHibernateDAO() {
super(Product.class);
}
}
Or if you prefer to use HQL instead of Criteria API you can rewrite search method as:
public T findOne(String paramName, Object paramValue) {
Session session = sessionFactory.getCurrentSession();
StringBuilder queryText = new StringBuilder();
queryText.append("from ");
queryText.append(clazz.getSimpleName());
queryText.append(" where ");
queryText.append(paramName);
queryText.append("=:");
queryText.append(PARAM_VALUE_PARAMETER);
#SuppressWarnings("unchecked")
T fetchedObject = (T) session.createQuery(queryText.toString()).setParameter(PARAM_VALUE_PARAMETER, paramValue).uniqueResult();
return fetchedObject;
}
In this article you can find very good description how to create generic DAO with hibernate (Or if you prefer JPA there are also described how to do this with JPA).
Or if you prefer to use JDBC for data access I recommend you to look at Spring's JdbcTemplate. It simplifies development a lot. Here how you can implement universal finder using JdbcTemplate:
#Repository
#Transactional
public class ProductJDBCDAO implements DAO<Product> {
private static final String TABLE_NAME = "product";
#Autowired
private JdbcTemplate jdbcTemplate;
public Product findOne(String paramName, Object paramValue) {
RowMapper<Product> rowMapper = new RowMapper<Product>(){
public Product mapRow(ResultSet rs, int rowNum) throws SQLException {
long productId = rs.getLong("product_id");
// Other properties
Product product = new Product(...);
return product;
}
};
StringBuilder queryText = new StringBuilder();
queryText.append("select * from ");
queryText.append(TABLE_NAME);
queryText.append(" where ");
queryText.append(paramName);
queryText.append("=?");
Product fetchedObject = jdbcTemplate.queryForObject(queryText.toString(), rowMapper, paramValue);
return fetchedObject;
}
// Other CRUD methods
}
Ass you can see in all examples you don't need explicitly specify parameter type, you just add it as Object parameter.
If you will work with direct JDBC in such case I recommend you to use PreparedStatement and it's setObject(..) method. Query text will be similar to shown in the example with JdbcTemplate.

Resources