Mockito mock Java #Value with spring boot - java

Hi I have this simple code for my Spring Boot Project:
#Component
public class UserRowMapper implements RowMapper<User> {
#Value("${bug.value}")
private String id;
#Value("${wrong.value}")
private String userName;
#Override
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
return User.builder()
.id(rs.getInt(id))
.userName(rs.getString(userName)).build();
}
}
what I want is to create a simple Mockito Test that will check #Value strings like so:
#ExtendWith(MockitoExtension.class)
class UserRowMapperTest {
#Mock
Environment environment;
#Mock
ResultSet resultSet;
#InjectMocks
UserRowMapper userRowMapper;
#Test
void testMapRow() {
when(environment.getProperty("user.id")).thenReturn("id");
when(environment.getProperty("user.userName")).thenReturn("userName");
try {
final User user = userRowMapper.mapRow(resultSet, anyInt());
// check if its ok
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
But I can't find a simple way to check if the value I injected is what I expect.
any ideas?

Unfortunately, there is no mocking mechanism for Spring's #Value. However, you can use a simple workaround using ReflectionUtils that serves for this purpose according to the JavaDoc:
ReflectionTestUtils is a collection of reflection-based utility methods for use in unit and integration testing scenarios.
There are often times when it would be beneficial to be able to set a non-public field, invoke a non-public setter method, or invoke a non-public configuration or lifecycle callback method when testing code involving
ReflectionTestUtils.setField(userRowMapper, "id", "my-id-value");
ReflectionTestUtils.setField(userRowMapper, "userName", "my-userName-value");
JavaDoc for ReflectionTestUtils#setField(Object, String, Object).

Add getter methods for id and userName fields instead of mocking Environment class.
#Component
public class UserRowMapper implements RowMapper<User> {
#Value("${bug.value}")
private String id;
#Value("${wrong.value}")
private String userName;
#Override
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
return User.builder()
.id(rs.getInt(getId()))
.userName(rs.getString(getUserName())).build();
}
public String getId() {
return id;
}
public String getUserName() {
return userName;
}
}
While mocking:
Mockito.when(userRowMapper.getId()).thenReturn("id");
Mockito.when(userRowMapper.getUserName()).thenReturn("userName");
Also, you can use TestPropertySource annotation to provide altogether different properties file:
#SpringBootTest
#TestPropertySource(locations = "/application2.properties")
public class TestClassTest {
#Autowired
TestClass testClass;
#Test
public void test() {
assertEquals("id", testClass.getId());
}
}

I would rather suggest to you to do not use inline #Value annotation on the consumer class. As you have seen, the class testability decreases.
You can solve your problem simply creating a #Configuration bean and injecting it to the UserRowMapper class. In this way, using DI you can easily mock the configuration in your tests.
See below a naïve implementation.
#Configuration
public class UserRowMapperConfiguration {
#Value("${bug.value}")
private String id;
#Value("${wrong.value}")
private String userName;
public String getId() {
return id;
}
public String getUserName() {
return userName;
}
}
#Component
public class UserRowMapper implements RowMapper<User> {
private UserRowMapperConfiguration configuration;
public UserRowMapper (UserRowMapperConfiguration configuration) {
this.configuration = configuration;
}
#Override
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
return User.builder()
.id(rs.getInt(this.configuration.getId()))
.userName(rs.getString(this.configuration.getUserName())).build();
}
}
#ExtendWith(MockitoExtension.class)
class UserRowMapperTest {
#Mock
UserRowMapperConfiguration configuration;
#Mock
ResultSet resultSet;
#InjectMocks
UserRowMapper userRowMapper;
#Test
void testMapRow() {
when(configuration.getId()).thenReturn("id");
when(configuration.getUserName()).thenReturn("userName");
try {
final User user = userRowMapper.mapRow(resultSet, anyInt());
// check if its ok
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}

As thepaoloboi suggested use a configuration class to hold all your configs.
Now to test that your config is pointing to the right #Value key, you create an integration test by simply loading that object using spring without loading the whole context. That way it'll be as fast as a unit test.
Here's an example:
#ExtendWith(SpringExtension.class)
#Import(UserRowMapperConfiguration.class)
#TestPropertySource(properties = { "user.id=id" , "user.userName=userName"})
class UserRowMapperConfigurationTest {
#Autowired
UserRowMapperConfiguration userRowMapperConfiguration;
#Test
void test() {
assertEquals("id",userRowMapperConfiguration.getId());
assertEquals("userName",userRowMapperConfiguration.getUserName());
}
}
and Configuration class:
#Configuration
public class UserRowMapperConfiguration {
#Value("${bug.value}")
private String id;
#Value("${wrong.value}")
private String userName;
public String getId() {
return id;
}
public String getUserName() {
return userName;
}
}

Related

How to inject value to a bean in spring test?

i have a question here, please give some ideas.
I have two beans. FaceComparisonServerImpl depends on FaceServer.
When i want to test. I want to change the String in my 'FaceServer' bean.
#Service
public class FaceComparisonServerImpl implements FaceComparisonServer {
#Autowired
private FaceServer faceServer;
#Override
public FaceComparsionInfo getServerInfo() {
String serverInfo = faceServer.getServerInfo();
...
}
}
#Component
public class FaceServer {
#Autowired
private RestTemplate restTemplate;
//Not final, just to test.
private String version = "1.0";
private static final String CODE = "code";
private static final String MESSAGE = "message";
//Final
private static final String SERVER_URL = "http://127.0.0.1:8066/api/ZKComparison";
}
Bellow is my test code.
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = TestConfig.class)
public class FaceServerTestByTyler {
#Autowired
private FaceComparisonServer faceComparisonServer;
#Test
public void getServerInfo(){
//How can i modify the value of SERVER_URL in faceServer?
FaceComparsionInfo serverInfo = faceComparisonServer.getServerInfo();
System.out.println(serverInfo);
}
}
My question is:
How can i modified the value of 'version' and 'SERVER_URL' in #Bean(faceServer)?
Thanks you!
You need create FaceServer mock bean for test configuration.
And override required methods
#Configuration
Class TestConfig{
#Bean
#Primary
public FaceServer faceServer() {
return new FaceServer() {
#override
public String getServerInfo(){
return "required info";
}
};
}
}
The easiest way to customize the values is to make them Spring properties:
#Component
public class FaceServer {
#Value("${faceServer.version}")
private String version;
#Value("${faceServer.url}")
private String serverUrl;
// ...
}
You can either have default values for the #Value annotations or use some default property values in application.yml.
Now just override those properties in your test with the values you want:
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = TestConfig.class)
#TestPropertySource(properties = {
"faceServer.version=1.0",
"faceServer.url=http://127.0.0.1:8066/api/ZKComparison"
})
public class FaceServerTestByTyler {
#Autowired
private FaceComparisonServer faceComparisonServer;
// ...
}
However...
The second option is to make your classes more unit-testable. Prefer construction injection over field injection, and you can test your classes more independently.
#Service
public class FaceComparisonServerImpl implements FaceComparisonServer {
private final FaceServer faceServer;
public FaceComparisonServerImpl(FaceServer faceServer) {
this.faceServer = faceServer;
}
#Override
public FaceComparsionInfo getServerInfo() {
String serverInfo = faceServer.getServerInfo();
// ...
}
}
This now becomes unit-testable:
public class FaceServerTestByTyler {
private FaceComparisonServer faceComparisonServer;
private FaceServer faceServer;
#BeforeEach
public setup() {
faceServer = mock(FaceServer.class);
faceComparisonServer = new FaceComparisonServer(faceServer);
}
#Test
public void getServerInfo() {
when(faceServer.getServerInfo()).thenReturn(xxx);
// ...
}
}
The second option ends up with a test that runs much faster than any solutions that suggest to create a mock bean through a test configuration.

How to read spring boot property file in service class method called by unit test

I have spring boot application. I want to write some unit test for methods in service class.
I can load Environment variable and get properties in unit test class but can't do it in service class. Environment in service class is always null when reaching it from unit tests. It work when reaching it from application.
SomethingServiceTest.java
#RunWith(SpringRunner.class)
#DataJpaTest
#TestPropertySource(value = "/application.properties")
public class SomethingServiceTest {
private ISomethingService m_SomethingService;
#PostConstruct
public void setup() {
m_SomethingService = new SomethingService();
m_SomethingService.setSomethingRepository(somethingRepository);
// somethingRepository is mocked class, probably not important
}
#Test
public void test_somethingMethod() {
System.out.println(env.getProperty("some.property"));
//env here is full and i get wanted property
m_uploadService.doSomething();
}
ISomethingService.java
public interface ISomethingService {
doSomething();
}
SomethingService.java
#Service
public class SomethingService implements ISomethingService {
#Value("${some.property}")
private String someProperty;
private ISomethingRepository somethingRepository;
#Autowired
public ISomethingRepository getSomethingRepository() {
return somethingRepository;
}
public void setSomethingRepository(ISomethingRepository p_somethingRepository) {
somethingRepository = p_somethingRepository;
}
#Autowired
private Environment env;
#Override
#Transactional
public String doSomething(){
System.out.println(env.getProperty("some.property"));
//env is null here
return someProperty;
}
}

Full validation test in Spring Boot, injection failing

Hello everyone I wanted to tested the full validation of a Request in my Spring Boot application I mean no testing one validator at a time but all of them on the target object)
First I have my object :
public class UserCreationRequest {
#JsonProperty("profileId")
#NotNull
#ValidProfile
private Integer profileId;
}
Then my Validator (#ValidProfile):
#Component
public class ProfileValidator implements ConstraintValidator<ValidProfile, Integer> {
#Autowired
private IProfileService profileService;
#Autowired
private IUserRestService userRestService;
#Override
public void initialize(ValidProfile constraintAnnotation) {
}
#Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
if (value == null) {
return true;
}
RestUser restUser = userRestService.getRestUser();
ProfileEntity profileEntity = profileService.getProfile(value, restUser.getAccountId());
return profileEntity != null;
}
}
Now I write my unit test :
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {ValidationTestConfiguration.class})
public class UserCreationRequestValidationTest {
private static LocalValidatorFactoryBean localValidatorFactory;
#Autowired
private IUserService userService;
#Autowired
private IProfileService profileService;
#Autowired
private IUserRestService restService;
#BeforeClass
public static void createValidator() {
localValidatorFactory = new LocalValidatorFactoryBean();
localValidatorFactory.setProviderClass(HibernateValidator.class);
localValidatorFactory.afterPropertiesSet();
}
#AfterClass
public static void close() {
localValidatorFactory.close();
}
#Test
public void validateUserCreationRequestStringfields() {
UserCreationRequest userCreationRequest = new UserCreationRequest();
/* Here fill test object*/
when(userService.getUser(any(Integer.class), any(Integer.class))).thenReturn(new UserEntity());
when(profileService.getProfile(any(Integer.class), any(Integer.class))).thenReturn(new ProfileEntity());
when(restService.getRestUser()).thenReturn(new RestUser());
Set<ConstraintViolation<UserCreationRequest>> violations
= localValidatorFactory.validate(userCreationRequest);
assertEquals(violations.size(), 8);
}
}
and my TestConfiguration is like that :
#Configuration
public class ValidationTestConfiguration {
#Bean
#Primary
public IProfileService profileService() {
return Mockito.mock(IProfileService.class);
}
#Bean
#Primary
public IUserRestService userRestService() { return Mockito.mock(IUserRestService.class); }
}
On execution I can see that in the test itself the injection works :
restService is mapped to "Mock for IUserRestService"
But in my validator it is not injected, userRestService is null.
Same thing for ProfileService
I tried several things seen here, nothing works (code is running, only test conf is failing)
This is because you do not produce the Validator bean so it can be injected.
As you manually instantiate the LocalValidatorFactoryBean, it cannot access to the spring DI defined for this test.
You should produce instead a bean for the Validator, or even reference an existing spring configuration to do so.

Getting null pointer in static method Junit

I have following service
public UserActivityLog save(UserActivityLog userActivityLog){
LOGGER.debug("User activity save called.");
userActivityLog.setCreatedByUser(User.getUser().getUserId());
userActivityLog.setCreationTime(new Date());
return activityLogRepository.save(userActivityLog);
}
I had written following Junit for test this but getting null pointer for User
#RunWith(MockitoJUnitRunner.class)
public class ActivityHistoryServiceTest {
#InjectMocks
private ActivityHistoryService activityHistoryService;
#Mock
private ActivityLogRepository activityLogRepository;
#Test
public void testSave() {
UserActivityLog userActivityLog = new UserActivityLog();
/* Called some setter methods to set value here*/
UserProfile profile = new UserProfile();
Mockito.when(User.getUser()).thenReturn(profile);
Assert.assertNotNull(activityLogRepository.save(userActivityLog));
}
}
User class is like
public class User {
private User(){}
public static UserProfile getUser(){
/*some logic*/
return userProfile;
}
}
Please help for this. thanks in advance.
Static methods are useful when they are useful. Use them as a replace for singletons is not a very good practice. Instead of what you have your code could look like this:
public class MyClass {
private final Logger logger;
private final UserProvider userProvider;
private final DateProvider dateProvider;
/** Constructor that sets all attributes */
public MyClass(...) {...}
public UserActivityLog save(UserActivityLog userActivityLog){
LOGGER.debug("User activity save called.");
userActivityLog.setCreatedByUser(userProvider.get().getUserId());
userActivityLog.setCreationTime(dateProvider.get());
return activityLogRepository.save(userActivityLog);
}
}
Provider classes are quite simple. They just have a get() method that returns an instance of required type:
public class DateProvider {
public Date get() {
return new Date();
}
}
public class UserProvider {
public User get() {
...
}
}
With this setup you can easily mock the dependencies
You need PowerMock to mock the static methods.
Refer:
https://blog.codecentric.de/en/2016/03/junit-testing-using-mockito-powermock/

Spring service unit testing using mockito

Until now answers from SO has been utterly satisfying for my problems. I'm learning unit testing with Junit and Mockito and I want to test my service class which is a part of my Spring web app. I read many tutorials and articles and I still have problems to write proper unit tests for my service layer. I would like to know answers for my questions, but first I paste some code:
Service class
public class AccountServiceImpl implements AccountService {
#Autowired
AccountDao accountDao, RoleDao roleDao, PasswordEncoder passwordEncoder, SaltSource saltSource;
#PersistenceContext
EntityManager entityManager;
public Boolean registerNewAccount(Account newAccount) {
entityManager.persist(newAccount);
newAccount.setPassword(passwordEncoder.encodePassword(newAccount.getPassword(), saltSource.getSalt(newAccount)));
setRoleToAccount("ROLE_REGISTERED", newAccount);
return checkIfUsernameExists(newAccount.getUsername());
}
public void setRoleToAccount(String roleName, Account account) {
List<Role> roles = new ArrayList<Role>();
try {
roles.add(roleDao.findRole(roleName));
} catch(RoleNotFoundException rnf) {
logger.error(rnf.getMessage());
}
account.setRoles(roles);
}
public Boolean checkIfUsernameExists(String username) {
try {
loadUserByUsername(username);
} catch(UsernameNotFoundException unf) {
return false;
}
return true;
}
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
try {
Account loadedAccount = accountDao.findUsername(username);
return loadedAccount;
} catch (UserNotFoundException e) {
throw new UsernameNotFoundException("User: " + username + "not found!");
}
}
}
My unfinished test class
#RunWith(MockitoJUnitRunner.class)
public class AccountServiceImplTest {
private AccountServiceImpl accountServiceImpl;
#Mock private Account newAccount;
#Mock private PasswordEncoder passwordEncoder;
#Mock private SaltSource saltSource;
#Mock private EntityManager entityManager;
#Mock private AccountDao accountDao;
#Mock private RoleDao roleDao;
#Before
public void init() {
MockitoAnnotations.initMocks(this);
accountServiceImpl = new AccountServiceImpl();
ReflectionTestUtils.setField(accountServiceImpl, "entityManager", entityManager);
ReflectionTestUtils.setField(accountServiceImpl, "passwordEncoder", passwordEncoder);
ReflectionTestUtils.setField(accountServiceImpl, "saltSource", saltSource);
ReflectionTestUtils.setField(accountServiceImpl, "accountDao", accountDao);
ReflectionTestUtils.setField(accountServiceImpl, "roleDao", roleDao);
}
#Test
public void testRegisterNewAccount() {
Boolean isAccountCreatedSuccessfully = accountServiceImpl.registerNewAccount(newAccount);
verify(entityManager).persist(newAccount);
verify(newAccount).setPassword(passwordEncoder.encodePassword(newAccount.getPassword(), saltSource.getSalt(newAccount)));
assertTrue(isAccountCreatedSuccessfully);
}
#Test
public void testShouldSetRoleToAccount() throws RoleNotFoundException{
Role role = new Role(); //Maybe I can use mock here?
role.setName("ROLE_REGISTERED");
when(roleDao.findRole("ROLE_REGISTERED")).thenReturn(role);
accountServiceImpl.setRoleToAccount("ROLE_REGISTERED", newAccount);
assertTrue(newAccount.getRoles().contains(role));
}
}
Questions:
What is the best way to make unit tests where I have methods in methods like in my service class? Can I test them separately like above? [I divided my code into few methods to have cleaner code]
Is testRegisterNewAccount() good unit test for my service method? Test is green however I am not sure about it.
I am getting failure in my testShouldSetRoleToAccount. What am I doing wrong?
How to test checkIfUsernameExists?
Maybe someone will help me with this because I spent a couple of days and I didn't make a progress :(
UPDATE
Finished test class
#RunWith(MockitoJUnitRunner.class)
public class AccountServiceImplTest extends BaseTest {
private AccountServiceImpl accountServiceImpl;
private Role role;
private Account account;
#Mock private Account newAccount;
#Mock private PasswordEncoder passwordEncoder;
#Mock private SaltSource saltSource;
#Mock private EntityManager entityManager;
#Mock private AccountDao accountDao;
#Mock private RoleDao roleDao;
#Before
public void init() {
accountServiceImpl = new AccountServiceImpl();
role = new Role();
account = new Account();
ReflectionTestUtils.setField(accountServiceImpl, "entityManager", entityManager);
ReflectionTestUtils.setField(accountServiceImpl, "passwordEncoder", passwordEncoder);
ReflectionTestUtils.setField(accountServiceImpl, "saltSource", saltSource);
ReflectionTestUtils.setField(accountServiceImpl, "accountDao", accountDao);
ReflectionTestUtils.setField(accountServiceImpl, "roleDao", roleDao);
}
#Test
public void testShouldRegisterNewAccount() {
Boolean isAccountCreatedSuccessfully = accountServiceImpl.registerNewAccount(newAccount);
verify(entityManager).persist(newAccount);
verify(newAccount).setPassword(passwordEncoder.encodePassword(newAccount.getPassword(), saltSource.getSalt(newAccount)));
assertTrue(isAccountCreatedSuccessfully);
}
#Test(expected = IllegalArgumentException.class)
public void testShouldNotRegisterNewAccount() {
doThrow(new IllegalArgumentException()).when(entityManager).persist(account);
accountServiceImpl.registerNewAccount(account);
}
#Test
public void testShouldSetRoleToAccount() throws RoleNotFoundException {
when(roleDao.findRole(anyString())).thenReturn(role);
accountServiceImpl.setRoleToAccount("ROLE_REGISTERED", account);
assertTrue(account.getRoles().contains(role));
}
#Test
public void testShouldNotSetRoleToAccount() throws RoleNotFoundException {
when(roleDao.findRole(anyString())).thenThrow(new RoleNotFoundException());
accountServiceImpl.setRoleToAccount("ROLE_RANDOM", account);
assertFalse(account.getRoles().contains(role));
}
#Test
public void testCheckIfUsernameExistsIsTrue() throws UserNotFoundException {
when(accountDao.findUsername(anyString())).thenReturn(account);
Boolean userExists = accountServiceImpl.checkIfUsernameExists(anyString());
assertTrue(userExists);
}
#Test
public void testCheckIfUsernameExistsIsFalse() throws UserNotFoundException {
when(accountDao.findUsername(anyString())).thenThrow(new UserNotFoundException());
Boolean userExists = accountServiceImpl.checkIfUsernameExists(anyString());
assertFalse(userExists);
}
#Test
public void testShouldLoadUserByUsername() throws UserNotFoundException {
when(accountDao.findUsername(anyString())).thenReturn(account);
Account foundAccount = (Account) accountServiceImpl.loadUserByUsername(anyString());
assertEquals(account, foundAccount);
}
#Test(expected = UsernameNotFoundException.class)
public void testShouldNotLoadUserByUsername() throws UserNotFoundException {
when(accountDao.findUsername(anyString())).thenThrow(new UsernameNotFoundException(null));
accountServiceImpl.loadUserByUsername(anyString());
}
}
Question 1 - You've got a couple of options here.
Option 1 - write separate tests for each behaviour of each public method, based on what is required for that behaviour. This keeps each test clean and separate, but it does mean that the logic in the secondary methods (such as checkIfUsernameExists) will be exercised twice. In a sense, this is unnecessary duplication, but one advantage of this option is that if you change the implementation, but not the required behaviour, you'll still have good tests based on the behaviour.
Option 2 - use a Mockito Spy. This is a little like a mock, except that you create it from a real object, and the default behaviour of it is that all the methods run as usual. You can then stub out and verify the secondary methods, in order to test the methods that call them.
Question 2 - This looks like a good test for the "success" case of registerNewAccount. Please think about what circumstances would cause registerNewAccount to fail and return false; and test this case.
Question 3 - I haven't had a good look at this; but try running with the debugger, and find out at which point your objects differ from what you expect. If you can't work it out, post again and I'll have another look.
Question 4 - To test the negative case, stub your mock of the AccountDao to throw the required exception. Otherwise, see my answers for question 1.

Categories

Resources