I'm currently on a course learning Spring-boot and I'm stuck with testing a project - any help is much appreciated as I'm a beginner here.
I have a rest controller test, using Mockito that appears to be ignoring "ThenReturn" when a method is invoked using Mockito.when().
Here is the whole class:
package com.example.demo.controllers;
import com.example.demo.TestUtils;
import com.example.demo.model.persistence.AppUser;
import com.example.demo.model.persistence.repositories.CartRepository;
import com.example.demo.model.persistence.repositories.UserRepository;
import com.example.demo.model.requests.CreateUserRequest;
import org.junit.Before;
import org.junit.Test;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import java.util.Optional;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
public class UserControllerTest {
private UserController userController;
private UserRepository userRepository = mock(UserRepository.class);
private CartRepository cartRepository = mock(CartRepository.class);
private BCryptPasswordEncoder bCryptPasswordEncoder = mock(BCryptPasswordEncoder.class);
#Before
public void initTest(){
userController = new UserController();
TestUtils.injectObjects(userController, "userRepository", userRepository);
TestUtils.injectObjects(userController, "cartRepository", cartRepository);
TestUtils.injectObjects(userController, "bCryptPasswordEncoder", bCryptPasswordEncoder);
AppUser appUser = TestUtils.getAppUser();
when(userRepository.findById(0L)).thenReturn(Optional.of(appUser));
when(bCryptPasswordEncoder.encode("testPassword")).thenReturn("hashedPassword");
}
#Test
public void testFindUserById(){
ResponseEntity<AppUser> response = userController.findById(0L);
System.out.println(response);
}
#Test
public void testCreateUser() throws Exception{
CreateUserRequest createUserRequest = new CreateUserRequest();
createUserRequest.setUsername("testUser");
createUserRequest.setPassword("testPassword");
createUserRequest.setConfirmPassword("testPassword");
ResponseEntity<AppUser> response = userController.createUser(createUserRequest);
assertNotNull(response);
assertEquals(200, response.getStatusCodeValue());
AppUser createdUser = response.getBody();
assertNotNull(createdUser);
assertEquals(0, createdUser.getId());
assertEquals("testUser", createdUser.getUsername());
assertEquals("hashedPassword", createdUser.getPassword());
}
}
The test called "testCreateUser" passes without a problem. It's the test called "testFindUserById" that is giving me a problem.
Here is the controller method I'm trying to test (all working fine when tested in Postman):
public ResponseEntity<AppUser> findById(#PathVariable Long id) {
try{
log.info("UserIDSearch = " + id);
System.out.println("UserIDSearch = " + id);
Optional<AppUser> optionalAppUser = userRepository.findById(id);
if(optionalAppUser.isPresent()){
log.info("UserIdFound = " + id);
return ResponseEntity.ok(optionalAppUser.get());
}else{
throw new ApiException(ExceptionTypes.SEARCHUSER, id.toString());
}
}catch(ApiException a){
return ResponseEntity.notFound().build();
}
}
The repository being mocked in the test class is just a straightforward JpaRepository:
public interface UserRepository extends JpaRepository<AppUser, Long> {
Optional<AppUser> findByUsername(String username);
public Optional<AppUser> findById(long id);
}
The output I get from running the testFindUserById() test is the following:
UserIDSearch = 0
<404 NOT_FOUND Not Found,[]>
I guess what I'm trying to achieve here is that the test uses the when().thenReturn() to simulate an OK response from the mocked userRepository, but instead it actually performs the search and returns the "Not found". Can anyone help? Thanks so much!
Seems to be a problem with autoboxing. Change the method public Optional<AppUser> findById(long id); to accept Long instead.
I think #Janar is correct, but I'll explain why.
When you write "when(userRepository.findById(0L)).thenReturn(Optional.of(appUser))", what Mockito does is set start monitoring calls to the indicated method (findById) and comparing the argument to "0L", which is a Java long int, which is not an object.
The actual method takes a java.lang.Long, which is an object. The two are not equal.
You can fix it by changing the findById method, as #Janar suggests, but that is not what I would do. I assume that the production code is using a java.lang.Long for a practical reason. Instead, I would change the test to:
when(userRepository.findById(Long.valueOf(0L))).thenReturn(Optional.of(appUser));
My case was not the same of your findById but I hope it helps.
Summary:
mockito is sensible with the passed arguments and it works better with simple or primitive data types
On my case, this sentence was ignored:
doReturn(result).when(scriptExecutor).
runScript(connection, new StringReader("selec * from dual;"));
After some hours of attempts, I change the definition of runScript to receive a simple string instead StringReader.
From this:
public ArrayList runScript(Connection conn, StringReader reader)
To this:
public ArrayList runScript(Connection conn, String query)
Then the mocked sentence was picked and everything works as expected:
doReturn(result).when(scriptExecutor).
runScript(connection, "selec * from dual;");
Basically if I use a StringReader, didn,t work. But with a simple String, it works!!
Related
I am having issue with Mocking a JDBC call using the MockitoJUnitRunner.
Somehow Mockito is not mocking the actual call even though I have below subbing line into the test class.
when(readOnlyJdbcTemplate.query(anyString(), any(Object[].class), any(int[].class), any(FeatureCollectionResponseExtractor.class))).thenReturn(actual);
Very similar mocking is working in another class for very similar type of method. The only difference between them is my other class does have 3 parameters instead of 4 parameters. Below is the code which is actually mocking successfully for different class.
when(readOnlyJdbcTemplate.query(anyString(), any(Object[].class), any(FeaturesResultExtractor.class))).thenReturn(actual);
Below is my actual code.
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.inject.Inject;
import javax.inject.Named;
import java.net.HttpURLConnection;
import java.sql.Types;
import static com.accounts.features.utils.Constants.INTERNAL_SERVER_ERROR;
#Profile
#Log
#Named("featureLibraryDao")
public class FeatureLibraryDaoImpl implements FeatureLibraryDao {
private static final Logger LOGGER = LogManager.getLogger(FeatureLibraryDaoImpl.class);
#Value("${feature.library.function.sql.query}")
private String sqlSelectQuery;
#Inject
#Named("readOnlyJdbcTemplate")
private JdbcTemplate readOnlyJdbcTemplate;
#Override
public FeatureCollectionDTO getFeaturesData(FeatureRequest request) {
try {
int[] argTypes = new int[] { Types.BIGINT, Types.VARCHAR, Types.SMALLINT};
return readOnlyJdbcTemplate.query(sqlSelectQuery, new Object[] {
Long.parseLong(request.getAccountId()), request.getRequestedFeatures(), request.getApplicationSuffix()
}, argTypes,
new FeatureCollectionResponseExtractor(request));
} catch (CustomException cbe) {
throw cbe;
} catch (Exception ex) {
LOGGER.error("getFeaturesData method failed with error message:{}", ex.getMessage(), ex);
CustomErrorCode error = new CustomErrorCode(INTERNAL_SERVER_ERROR);
error.setDeveloperText(ex.getMessage());
throw new CustomSystemException(error, HttpURLConnection.HTTP_INTERNAL_ERROR);
}
}
}
and below is my test class.
#RunWith(MockitoJUnitRunner.class)
public class FeatureLibraryDaoImplTest {
#InjectMocks
private FeatureLibraryDaoImpl dao;
#Mock
private JdbcTemplate readOnlyJdbcTemplate;
private List<String> features = Arrays.asList("excl_clsd_ind_only", "excl_chrgoff_ind_only", "excl_dsput_ind_only");
#Test
public void getFeaturesDataWhenSuccess() {
//given
FeatureRequest request = getFeatureRequest();
FeatureCollectionDTO actual = new FeatureCollectionDTO(features);
when(readOnlyJdbcTemplate.query(anyString(), any(Object[].class), any(int[].class), any(FeatureCollectionResponseExtractor.class))).thenReturn(actual);
//when
FeatureCollectionDTO dto = dao.getFeaturesData(request);
//then
assertThat(dto, notNullValue());
}
}
Any suggestion about what is wrong here? Is there any issue with any(int[].class) ?
I do see you are not passing the sql query sqlSelectQuery value during the test case, But during mock you specified anyString() so it must be some value but not null. Since you are using spring project, you can use ReflectionTestUtils to set the field value for object
#Before
public void setUp() {
ReflectionTestUtils.setField(dao, "sqlSelectQuery", "query");
}
Hey Guys thanks much for all your suggestions. So I found that Test code is perfectly fine. Some how #Value tag wasn't injecting the actual value the sqlSelectQuery in the main code file.
#Value("${feature.library.function.sql.query}") private String sqlSelectQuery;
Instead of that I changed code to private String sqlSelectQuery = "${feature.library.function.sql.query}" and all test cases are passing.
Somehow sqlSelectQuery was't getting the value and hence Mockito wasn't mocking the actual method call. I am yet reviewing why #value is not working as it should be.
I am beyond frustrated because I can't figure this out. I've been over quite a lot of articles but i can't figure out how to solve this problem, and yet i think i overlook something very simple.
I have a class with a couple of endpoints, one of them is:
#GET
#Path("courses")
#Produces(MediaType.APPLICATION_JSON)
public Response courses(#QueryParam("token") String token) {
Integer studentId = iStudentDAO.getStudentIdByToken(token);
if(studentId == null){
return Response.status(403).build();
} else {
GetStudentsResponse studentCourses = iStudentDAO.getStudentCourses(studentId);
return Response.status(200).entity(courses.name).build();
}
}
This method should always return a Response.status. And thats exactly what i want to test. I know its not possible to unit tests the actual response (200 or 403) but i would like to know how to test if at least Response.status is returned. Im using Mockito for this in my Maven project.
#Test
public void testGetAllCourses () {
String token = "100-200-300";
StudentRequests studentRequests = mock(StudentRequests.class);
when(studentRequests.courses(token)).thenReturn(Response.class);
Response expected = Response.class;
assertEquals(expected, studentRequests.courses());
}
Could anyone explain me how to accomplish this? :)
You should be able to test your specific responses.
Assuming, the method courses is in a class Controller. And this is your class under test, your target should be to test the code here, which is the code that you have written in your controller.
Here is an example:
package test;
import static org.junit.Assert.*;
import org.apache.catalina.connector.Response;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
public class ControllerTest {
#Mock
IStudentDAO iStudentDAO;
#InjectMocks
Controller controller;
#Before
public void init() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testCoursesWhenStudentIDNotFound() {
Mockito.when(iStudentDAO.getStudentIdByToken("1234")).thenReturn(null);
Response response = controller.courses("1234");
assertEquals(403, response.getStatus())
}
}
Similarly, in the next test case, you can mock the IStudentDAO, to return a studentId and then courses for this studentId and validate that you do get them in the response.
I have a method that performs a test to see if the user is authorized and then has some other logic in it which I want to test, without actually logging in to authorize my user.
So, I have this static method OAuthUtil.retrieveAuthority() which returns a string, let's say "domain".
My constructor is something like
public ImplService(){
String authority = OAuthUtil.retrieveAuthority();
//do something else
}
And I have another method which is the one I'm actually trying to test, say getList().
retrieveAuthority() in turn can throw aWebApplicationException if the Subject is null, which it will always be, but I want to bypass this completely. So, I want my mock to return something ("domain") instead of throwing an exception. Is that possible?
So, my test is something like this right now, and it fails when the exception is encountered:
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.modules.junit4.PowerMockRunner;
#RunWith(PowerMockRunner.class)
public class TestMine {
public ImplService impl;
#Before
public void setUp() {
PowerMockito.mockStatic(OAuthUtil.class);
PowerMockito.when(OAuthUtil.retrieveAuthority()).thenReturn("domain");
ImplService impl = new ImplService();
}
#Test
public void getListTest() throws NotFoundException {
Response response = impl.getList();
}
}
Yes it's completely possible. You need to add:
#PrepareForTest({OAuthUtil.class})
public class TestMine { //above this line
I am trying to write a Junit unit test which mocks an interface that extends the spring-data-jpa interface CrudRepository<T, ID extends Serializable>. This interface provides the method
<S extends T> S save(S entity);
When I call this in my Expectations it wrongly records the return type as the type of the mocked interface rather than S (as resolved by my mocked interface) which causes a class cast exception.
I have debugged through and the generic signature it records is
<S:TT;>(TS;)TT;
There is a lot of code around figuring out what the return type is but eventually it comes up with the wrong answer. I'm fairly sure this is a bug but I hope someone can confirm this or tell me where I'm going wrong. One other thing to note is I am able to successfully mock other calls on the interface that just return T.
Jmockit: 1.16
JUnit: 4.12
spring-data-jpa: 1.7.1.RELEASE
Update with example
I have put together a scaled down test case that demonstrates the issue (below). In doing so I have uncovered a strange quirk. In the test I have two MyEntity objects which I use in the two expectations. If I change the save expectation to take and return the same objecct as the findOne call, the bug does not occur.
MyEntity.java
import javax.persistence.Entity;
#Entity
public class MyEntity
{
}
MyRepository.java
import org.springframework.data.repository.CrudRepository;
public interface MyRepository extends CrudRepository<MyEntity, Long>
{
}
DataAccess.java
public class DataAccess
{
private final MyRepository repository;
public DataAccess(MyRepository repository)
{
this.repository = repository;
}
public MyEntity resaveEntity(long id)
{
MyEntity entity = repository.findOne(id);
return repository.save(entity);
}
}
DataAccessTest.java
import mockit.Expectations;
import mockit.Mocked;
import org.junit.Test;
public class DataAccessTest
{
#Test
public void testResaveEntity(#Mocked final MyRepository repository) throws Exception
{
final MyEntity myEntity = new MyEntity();
final MyEntity myEntity2 = new MyEntity();
new Expectations()
{
{
repository.findOne(1L);
result = myEntity;
repository.save(myEntity2);
result = myEntity2;
}
};
DataAccess dataAccess = new DataAccess(repository);
dataAccess.resaveEntity(1);
}
}
Exception
java.lang.ClassCastException: $Impl_MyRepository cannot be cast to MyEntity
at DataAccess.resaveEntity(DataAccess.java:16)
at DataAccessTest.testResaveEntity(DataAccessTest.java:27)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:74)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:211)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:67)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)
There is an error in the test. Where it reads
repository.save(myEntity2);
it should be
repository.save(myEntity1);
With this fix, the test should pass. On the other hand, that ClassCastException occurs because of a bug in JMockit, for which an issue was opened.
I got the following custom FieldSetMapper
import org.springframework.batch.item.file.mapping.FieldSetMapper;
import org.springframework.batch.item.file.transform.FieldSet;
import org.springframework.validation.BindException;
import de.BasicProperty;
public class PersonItemFieldSetMapper implements FieldSetMapper<PersonItem> {
#Override
public PersonItem mapFieldSet(final FieldSet fieldSet) throws BindException {
PersonItem person = new PersonItem();
person.setLastName(fieldSet.readString(BasicProperty.BASICPROP_LASTNAME));
person.setFirstName(fieldSet.readString(BasicProperty.BASICPROP_FIRSTNAME));
person.setEmployeeType(fieldSet.readString("EmployeeType"));
person.setFormOfAdress(fieldSet.readString(BasicProperty.BASICPROP_FORMOFADDRESS));
person.setEMail(fieldSet.readString(BasicProperty.BASICPROP_EMAIL));
person.setUpn(fieldSet.readString(BasicProperty.BASICPROP_UPN));
person.setWorkforceId(fieldSet.readString(BasicProperty.BASICPROP_WORKFORCE_ID));
person.setInstitute(fieldSet.readString(BasicProperty.BASICPROP_INSTITUTE));
person.setPhoto(fieldSet.readString(BasicProperty.BASICPROP_PHOTO));
return person;
}
}
At the moment i am trying to write a unit-test for this mapper.
First Question: Is this reasonable?
Second Question: How can i achieve this?
Yes you can write Test Cases for mapFieldSet()
You can always write such test cases to make sure your mapping are good!
// Using EasyMock to mock FieldSet
#Test
public void mapFieldSetTest()
{
FieldSet mockFieldSet = EasyMock.createMock(FieldSet.class);
EasyMock.expect(mockFieldSet.readString("LNAME")).andReturn("Doe");
EasyMock.expect(mockFieldSet.readString("FNAME")).andReturn("John");
EasyMock.expect(mockFieldSet.readString("ETYPE")).andReturn("PART-TIME");
EasyMock.expect(mockFieldSet.readString("FADDRESS")).andReturn("191, Santa Clara");
EasyMock.expect(mockFieldSet.readString("EMAIL")).andReturn("john.Doe#fb.com");
EasyMock.expect(mockFieldSet.readString("UPN")).andReturn("1111111111");
EasyMock.expect(mockFieldSet.readString("WFID")).andReturn("22222222");
EasyMock.expect(mockFieldSet.readString("INS")).andReturn("CA University College");
EasyMock.expect(mockFieldSet.readString("PIC")).andReturn("c:\\john_doe.jpg");
EasyMock.replay(mockFieldSet);
// call the method under test
PersonItem actual = fieldSetMapper.mapFieldSet(mockFieldSet);
EasyMock.verify(mockFieldSet);
// assert actual data by getters..
}