I'm unit testing with mockito and I get nulls in the repository, after doing a .save().
My test:
....
#BeforeEach
void setUp() {
// USUARIO
user = new User();
user.setUserName("Username A");
user.setPassword("Pass A");
user.setConfirmPassword("Pass A");
}
#MockitoSettings(strictness = Strictness.LENIENT)
#Test
void createUserNoExistValidConfirmation() throws Exception {
User userToPassParam = new User();
userToPassParam.setUserName("Username_Aa");
userToPassParam.setPassword("Pass A");
userToPassParam.setConfirmPassword("Pass A");
// checkUserNameAvailable
Mockito.when(userDaoRepository.findByUserName(userToPassParam.getUserName())).thenReturn(Optional.ofNullable(user));
// -- checkPasswordValid
Mockito.when(bCryptPasswordEncoder.encode(userToPassParam.getPassword())).thenReturn(user.getPassword());
// -- save
Mockito.when(userDaoRepository.save(userToPassParam)).thenReturn(user);
User userToCallServiceImpl = new User();
userToCallServiceImpl.setUserName("Username A");
userToCallServiceImpl.setPassword("Pass A");
userToCallServiceImpl.setConfirmPassword("Pass A");
User user = userServiceImpl.createUser(userToCallServiceImpl); // HERE GET NULLS
System.out.println("User: " + user);
System.out.println("this.user: " + this.user);
Assertions.assertEquals(user.getUserName(), this.user.getUserName());
}
.....
value of userToPassParam passed parameter:
the expected object
I only treat 3 values of the object, "userName, password, confirmPassword" the other values do not matter to me.
When I try to pass the test: userServiceImpl
#Override
public User createUser(User user) throws Exception {
if (checkUserNameAvailable(user) && checkPasswordValid(user)) {
// cogemos el estado de la DB para más adelante: user = save()
String encodePassword = bCryptPasswordEncoder.encode(user.getPassword());
user.setPassword(encodePassword);
user = userDaoRepository.save(user);
}
return user;
}
The user has a "before save" value. Right when it saves I get a null.
After repository.save(object) the object returned by the .save "mock object" is null.
I don't understand very well why the .save returns null, when in the "when" I specify what to return.
It's as if mockito didn't intercept the .save() to return the object.
Note: I use Junit 5, Mockito 4.6.1
I am expecting the .save() to return the "user" as I specified in my #BeforeEach void setUp() {...}.
Mockito.when(userDaoRepository.save(userToPassParam)).thenReturn(user);
That is, the intercept of the .save() so that it returns the object.
I have fixed this with an ArgumentMatchers. For some unknown reason, Mockito needs it.
import static org.mockito.ArgumentMatchers.any;
Mockito.when(userDaoRepository.save(any(User.class))).thenReturn(user);
Information source: here
Note: I have also implemented equals and hashCode overrides, but it doesn't detect them.
Related
I am trying to use Spring LDAP to retrieve and modify user information in an Active Directory server, but I can't retrieve a user record by dn so that I can modify it.
I am able to find the record by username with the LdapTemplate.search method. There is no dn attribute in the record, but distinguishedName looks like it should be correct. When I use LdapTemplate.lookupContext to retrieve the record by dn, however, the server says that it can't find the record by the dn that it just gave me. What am I doing wrong?
It seem wrong that the LdapTemplate search method doesn't give you a handle that you can use without doing a second query from the Active Directory. Is there a better way to do this?
I have created a sample Groovy application to demonstrate the problem. My Spring Boot application creates this class and then invokes the runTest method.
package edu.sunyjcc.gateway.ldap;
import javax.naming.Name;
import javax.naming.NamingException;
import javax.naming.directory.Attributes;
import javax.naming.ldap.LdapName;
import org.springframework.ldap.core.AttributesMapper;
import org.springframework.ldap.core.LdapTemplate;
import static org.springframework.ldap.query.LdapQueryBuilder.query;
import org.springframework.ldap.core.DirContextOperations;
public class ActiveDirectoryDNSample {
LdapTemplate ldapTemplate;
/** Attributes to fetch from server */
static attributeList = [
"sAMAccountName",
"distinguishedName",
"baseDn",
"userPrincipalName",
];
/** This will represent the record retrieved from Active Directory */
class Person {
/** Raw data from server */
Map attributes = [:];
/** Return the distinguished name */
String getDn() {
attributes?.distinguishedName;
}
String getUsername() {
attributes?.sAMAccountName;
}
String toString() {
"${this.username} <${this.getDn()}>"
}
/** Get a handle to the object from AD so we can modify it. This fails. */
def getContext() {
assert ldapTemplate;
println "in getContext()";
def dn = new LdapName(this.getDn());
println "...dn=$dn"
assert dn;
// The next line throws an exception.
DirContextOperations context = ldapTemplate.lookupContext(dn);
println "...context=$context"
}
}
/** Convert the attributes from AD into a Person object */
class RecordMapper implements AttributesMapper<Person> {
/** Create a Person object from the attribute map */
Person mapFromAttributes(Attributes attributes)
throws NamingException {
assert ldapTemplate;
Person prec = new Person(
ldapTemplate: ldapTemplate
);
attributeList.collect {
[attrName: it, attr: attributes.get(it)]
}.grep {it.attr}.each {
prec.attributes."${it.attrName}" = it.attr.get() as String;
}
return prec;
}
}
/** Get a user from Active Directory */
public List<Person> getByUsername(String username) throws Exception {
assert ldapTemplate;
AttributesMapper attrMapper = new RecordMapper();
assert attrMapper;
List s = ldapTemplate.search(
query().
where("sAMAccountName").is(username),
attrMapper
);
if (s == null) {
System.err.println("s is null");
}
return s?:[];
}
/** Try to fetch a record and get a modify context for it */
public runTest(String username) {
println "In ActiveDirectoryDNSample.runText($username)"
assert ldapTemplate;
def records = getByUsername(username);
println "Retrieved ${records?.size()} records";
records.each {println " $it"}
println "Now try to get the context for the records"
records.each {
person ->
println " getting context for $person";
def context = person.getContext();
println " context=$context"
}
}
public ActiveDirectoryDNSample(LdapTemplate ldapTemplate ) {
this.ldapTemplate = ldapTemplate;
}
}
In ActiveDirectoryDNSample.runText(testuser)
Retrieved 1 records
testuser <CN=Test User,CN=Users,DC=jccadmin,DC=sunyjcc,DC=edu>
Now try to get the context for the records
getting context for testuser <CN=Test User,CN=Users,DC=jccadmin,DC=sunyjcc,DC=edu>
in getContext()
...dn=CN=Test User,CN=Users,DC=jccadmin,DC=sunyjcc,DC=edu
and then it dies with a javax.naming.NameNotFoundException with the following data.
[LDAP: error code 32 - 0000208D: NameErr: DSID-03100238, problem 2001 (NO_OBJECT), data 0, best match of:
'CN=Users,DC=jccadmin,DC=sunyjcc,DC=edu'
\0]
Thanks for any help you can give me.
It turns out that there was, indeed, a better way. Instead of using an org.springframework.ldap.core.AttributesMapper in the search, you use org.springframework.ldap.core.ContextMapper.
In my example, I added a field to the Person class, which will hold a reference to the context.
DirContextOperations context;
Then I created a new class extending org.springframework.ldap.core.support.AbstractContextMapper.
class PersonContextMapper extends AbstractContextMapper {
#Override
protected Object doMapFromContext(DirContextOperations ctx) {
AttributesMapper attrMapper = new RecordMapper();
Person p = attrMapper.mapFromAttributes(ctx.attributes);
p.context = ctx;
return p;
}
}
When I passed it to the ldapTemplate.search method in the place of the AttributeMapper, I was able to use the context to update the Active Directory.
I want to update one variable for user.
Method:
fun blockUser(username: String): Boolean {
val user = userRepository.findUserByUsername(username)
if (user == null) {
throw UsernameNotFoundException("User not found by username: $username")
} else {
userRepository.blockUser(username)
}
val justToCheckIfUserIsUpdated = userRepository.findUserByUsername(username) //to be removed
return true
}
Query
#Modifying
#Transactional
#Query("update UserEntity c set c.isBlocked = TRUE WHERE c.username = :username")
fun blockUser(#Param("username") username: String)
Test:
#Test
fun `should block user when user exists`() {
//given
val userEntity = UserEntity(
username = "user",
phoneNumber = "500200300",
isBlocked = false,
email = "kupaa#wp.pl"
)
val userService = UserService(userDetailsRepository, userRepository, passwordEncoder)
userRepository.save(userEntity);
//when
userService.blockUser(userEntity.username);
//then
val blockedUser = userRepository.findUserByUsername(userEntity.username);
assertThat(blockedUser!!.isBlocked).isTrue()
}
While debugging you can see that isblocked is always true despite I updated it to true value.
Test ends with:
Expected :true
Actual :false
What I do wrong? I'm really confused.
When you do updates using JPQL, the updates go directly to the database. Hibernate doesn't magically update corresponding entities in the persistence context (the first level cache).
This userRepository.findUserByUsername(username) should get a user from the database (without the cache). But if you have a query cache enabled, the result can be loaded from the cache. You need to enable SQL logging and check the logs for that.
Also keep in mind that #Transactional above blockUser() method does nothing because spring boot creates a transaction for each test method. It doesn't mean that you have to remove #Transactional of course.
My Service code likes below:
import javax.transaction.Transactional;
#Service
public class UserServiceImpl implements UserService {
#Transactional
#Override
public void changeAuthorities(Long id, ChangeUserAuthoritiesRequest model) throws RecordNotFoundException {
Optional<User> userOptional = userRepository.findById(id);
if (userOptional.isPresent()) {
User user = userOptional.get();
long result = userAuthoritiesRepository.removeByUser(user);
// System.out.println(userAuthoritiesRepository.findByUser(user));
model.getAuthorityIds().stream().forEach(authorityId -> {
UserAuthority userAuthority = new UserAuthority();
Authority authority = authorityRepository.findById(authorityId).get();
userAuthority.setUser(user);
userAuthority.setAuthority(authority);
userAuthoritiesRepository.save(userAuthority);
});
} else {
throw new RecordNotFoundException("User not found with id: " + id);
}
}
}
The code means "delete all records with the given id and then add again the new ones" (the new ones may be the same the old ones).
My problem is method userAuthoritiesRepository.removeByUser(user) is not executed before new records are saved by userAuthoritiesRepository.save(userAuthority). So the app raises the exception:
Caused by: com.microsoft.sqlserver.jdbc.SQLServerException: Violation of UNIQUE KEY constraint 'user_authority_unique'. Cannot insert duplicate key in object 'dbo.user_authorities'. The duplicate key value is (1, 1).
at com.microsoft.sqlserver.jdbc.SQLServerException.makeFromDatabaseError(SQLServerException.java:258)
at com.microsoft.sqlserver.jdbc.SQLServerStatement.getNextResult(SQLServerStatement.java:1535)
at com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement.doExecutePreparedStatement(SQLServerPreparedStatement.java:467)
at com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement$PrepStmtExecCmd.doExecute(SQLServerPreparedStatement.java:409)
at com.microsoft.sqlserver.jdbc.TDSCommand.execute(IOBuffer.java:7151)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.executeCommand(SQLServerConnection.java:2478)
at com.microsoft.sqlserver.jdbc.SQLServerStatement.executeCommand(SQLServerStatement.java:219)
at com.microsoft.sqlserver.jdbc.SQLServerStatement.executeStatement(SQLServerStatement.java:199)
at com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement.executeUpdate(SQLServerPreparedStatement.java:356)
at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61)
at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeUpdate(HikariProxyPreparedStatement.java)
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:175)
... 177 more
There is nothing wrong with the #Transaction annotation or the order of execution of your code. Its just that the changes aren't pushed to the database. So ou are getting that error.
You will have to call the flush() method after the removeByUser() method call to push the changes to the database.
You can check this link for understanding why it is needed
I am running Junit test case for service layer but I am getting
org.junit.ComparisonFailure: expected:VendorEntity#3e60be48 but was:null
When vendorRepo.save(vendorEntity) method is called it returns null, I am not able to figure out why it is returning null. Below is my code.
#Autowired
private VendorSvc vendorSvc;
#MockBean
private VendorRepo vendorRepo;
#Test
public void testSaveVendorForm() {
VendorEntity vendorEntiy = getVendor();
Mockito.when(vendorRepo.save(vendorEntiy)).thenReturn(vendorEntiy);
// saveVendorForm return null
VendorEntity vendorEntity2 = vendorSvc.saveVendorForm(getVendorDto());
assertThat(vendorEntity2).isEqualTo(vendorEntiy);
}
After making some change in saveVendorForm which accept vendorEntity below code works but I don't want to pass entity class object to service layer as I want to create entity object in service layer and pass it to dao layer
#Test
public void testSaveVendorForm() {
VendorEntity vendorEntity = getVendor();
Mockito.when(vendorRepo.save(vendorEntity)).thenReturn(vendorEntity);
VendorEntity vendorEntity2 = vendorSvc.saveVendorForm(vendorEntity);
assertThat(vendorEntity2).isEqualTo(vendorEntity);
}
private VendorEntity getVendor() {
VendorEntity vendorEntity = new VendorEntity();
SocietyEntity societyEntity = new SocietyEntity();
societyEntity.setSocietyId(1L);
PincodeEntity pincodeEntity = new PincodeEntity();
pincodeEntity.setPincodeId(1L);
vendorEntity.setVendor("XYZ Cafe");
vendorEntity.setAddress("abc address");
vendorEntity.setEmailId("xyz#gmail.com");
vendorEntity.setContactNo1("123456");
vendorEntity.setContactNo2("123457");
vendorEntity.setSocietyId(societyEntity.getSocietyId());
vendorEntity.setPincodeId(pincodeEntity.getPincodeId());
vendorEntity.setWebsite("www.xyzabc.com");
vendorEntity.setCategoryId(2);
vendorEntity.setStatus(Constant.ACTIVE);
vendorEntity.setCreatedBy(1L);
vendorEntity.setCreatedDate(CommonUtil.getCurrentTimeStamp());
vendorEntity.setCreatedIp(Constant.DEFAULT_IP);
vendorEntity.setSocietyEntity(new SocietyEntity());
vendorEntity.setPincodeEntity(new PincodeEntity());
return vendorEntity;
}
#Override
public VendorEntity saveVendorForm(VendorDto vendorDto) {
VendorEntity vendorEntity = new VendorEntity();
// copy properties from (source,target)
BeanUtils.copyProperties(vendorDto,vendorEntity);
vendorEntity.setCreatedBy(vendorDto.getCreatedBy());
vendorEntity.setCreatedDate(vendorDto.getCreatedDate());
vendorEntity.setCreatedIp(vendorDto.getCreatedIp());
vendorEntity.setModifiedBy(vendorDto.getModifiedBy());
vendorEntity.setModifiedDate(vendorDto.getModifiedDate());
vendorEntity.setModifiedIp(vendorDto.getModifiedIp());
vendorEntity.setSocietyEntity(new SocietyEntity());
vendorEntity.setPincodeEntity(new PincodeEntity());
vendorEntity.setStatus(Constant.ACTIVE);
// below code returns null but works well when run in tomcat and form submitted through web browser
return vendorRepo.save(vendorEntity);
}
public interface VendorRepo extends JpaRepository<VendorEntity, Long> {
}
Can someone please tell me what is wrong in the code.
You are mocking save method for object vendorEntity but actaully passing a different object created via VendorDto object. Both are different object I guess which causing null in return.
Follow my comment on your test case(did not make any change except comments).
#Test
public void testSaveVendorForm() {
VendorEntity vendorEntiy = getVendor();
//Mocking the verndorRepo.save to return vendorEntiy when save is called with vendorEntiy
Mockito.when(vendorRepo.save(vendorEntiy)).thenReturn(vendorEntiy);
// saveVendorForm return null
// Actually passed a different object which may not be equal to vendorEntiy
VendorEntity vendorEntity2 = vendorSvc.saveVendorForm(getVendorDto());
assertThat(vendorEntity2).isEqualTo(vendorEntiy);
}
saveVendorForm may not be generating the exact VendorEntity object that we configured in mocking.
So if you make sure that getVendorDto() to VendorEntity trasnformation generates object similar to vendorEntity(the one which is getting created via getVendor method) then your test case would work as expected.
Similar objects means equals method should return true for given objects.
When I go to /confirmation-account link, in tomcat console I can see that if and else block is also executed. I can see:
print from ColorConsoleHelper.getGreenLog("loginView") and from ColorConsoleHelper.getGreenLog("confirmationAccountView")
This is really strange behavior. Why?
#RequestMapping(value = "/confirmation-account", method = RequestMethod.GET)
#Transactional
public ModelAndView displayConfirmationAccountPage(ModelAndView modelAndView, #RequestParam Map<String, String> requestParams) {
final int ACTIVE_USER = 1;
// find the user associated with the confirmation token
UserEntity userEntity = userService.findUserByConfirmationToken(requestParams.get("token"));
// this should always be non-null but we check just in case
if (userEntity!=null) {
// set the confirmation token to null so it cannot be used again
userEntity.setConfirmationToken(null);
// set enabled user
userEntity.setEnabled(ACTIVE_USER);
// save data: (token to null and active user)
saveAll(userEntity.getTrainings());
/*
RedirectAttributes won't work with ModelAndView but returning a string from the redirecting handler method works.
*/
modelAndView.addObject("successMessage", "Konto zostało pomyślnie aktywowane!");
modelAndView.setViewName("loginView");
ColorConsoleHelper.getGreenLog("loginView");
} else {
ColorConsoleHelper.getGreenLog("confirmationAccountView");
modelAndView.addObject("errorMessage", "Link jest nieprawidłowy...");
modelAndView.setViewName("confirmationAccountView");
}
return modelAndView;
}
public void saveAll(List<TrainingUserEntity> trainingUserEntityList) {
for ( TrainingUserEntity trainingUserEntity : trainingUserEntityList) {
entityManagerService.mergeUsingPersistenceUnitB(trainingUserEntity);
}
}
public void mergeUsingPersistenceUnitB(Object object) {
EntityManager entityManager = getEntityManagerPersistenceUnitB();
EntityTransaction tx = null;
try {
tx = entityManager.getTransaction();
tx.begin();
entityManager.merge(object);
tx.commit();
}
catch (RuntimeException e) {
if ( tx != null && tx.isActive() ) tx.rollback();
throw e; // or display error message
}
finally {
entityManager.close();
}
}
Below solution & explanation:
Because of /confirmation-account link is invoke twice, what is caused by dynamic proxy and #Transactional method annotated in controller It is mandatory to check how many displayConfirmationAccountPage method is invoked. It is workaround.
What do you think it is good or not to annotated #Transactional controller method?