I am trying to understand Mockito and Junit 4. I hava an interface named UserService which has a method named getUserInfo. I have written an implementation class UserServiceImpl to this interface.
I am using this getUserInfo method inside another class named UserInfoImpl. Now I am trying to Junit UserInfoImpl by Mocking UserServiceImpl. But instead of going to to the mock method, the call is going to the actual method. I have verified it by adding sysout to the actual method. I am not sure why it is not getting mocked. Can someone please throw some light.
Junit Class
public class Junit4WithMockito {
private UserInfoImpl userInfo;
#Mock
private UserService userService;
#Before
public void setUp() {
System.out.println("Inside Setup");
MockitoAnnotations.initMocks(this);
}
#Test
public void testUserInfoImpl() throws InterruptedException {
String cid="yu444";
userInfo = new UserInfoImpl();
userInfo.setUserService(userService);
when(userService.getUserInfo(cid)).thenReturn(new User("John Doe",33));
Assert.assertEquals("Peter",userInfo.getUserInfo(cid).getUsername());
}
}
UserInfoImpl Class
public class UserInfoImpl implements UserInfo {
private UserService userService;
public UserService getUserService() {
return userService;
}
public void setUserService(UserService userService) {
this.userService = userService;
}
#Override
public User getUserInfo(String username) {
try {
userService = new UserServiceImpl();
return userService.getUserInfo(username);
} catch (InterruptedException e) {
e.printStackTrace();
return null;
}
}
}
****** UserServiceImpl Class which I am trying to mock *********
public class UserServiceImpl implements UserService {
#Override
public User getUserInfo(String username) throws InterruptedException {
System.out.println("Inside the actual Service");
Thread.sleep(5000);
return new User("John Doe",33);
}
}
Error Message Below
You don't usually want to mock an implementation class, especially when there is an interface for it. The point of mocking is that you don't need (or want) the real implementation to be used.
So in your case, your #Mock should be
#Mock
private UserService userService;
Furthermore, you have this line in UserInfoImpl:
userService = new UserServiceImpl();
Obviously this replaces whatever instance of UserService you inject (including the mock). I'm guessing that's a mistake (maybe left around from an earlier attempt) since I can't think of any reason you'd really want to instantiate something that you're injecting.
Related
I am creating a Junit Unit Test to check the createAccount method in service that calls Service a helper method. Please find it below.
Service class
public class AccountServiceImpl {
#Autowired
AccountHelper accountHelper;
#Override
public Account createAccount(Account account) throws CustomerNotFoundException {
accountHelper.checkAccountTypeForCustomer(account);
return accountRepository.save(account);
}
}
Helper Class:
public void checkAccountTypeForCustomer(Account acc) throws CustomerNotFoundException {
Boolean customerExists = customerRepository.existsById(acc.getCustomerId());
if(!customerExists) {
throw new CustomerNotFoundException("604", Message.CUSTOMER_NOT_FOUND);
}
}
AccountServiceTest class
#ExtendWith(MockitoExtension.class)
public class AccountServiceTest {
#Mock
private AccountRepository accountRepository;
#Mock
private CustomerRepository customerRepository;
#Mock
private AccountHelper accountHelper;
#InjectMocks
private AccountService testService;
#Test
void testCreateAccount() throws CustomerNotFoundException {
Account account = Account.builder().
accountType(AccountType.SAVINGS).
openingBalance(BigDecimal.valueOf(3000)).
ifsc("IFSC1").
customerId(1).
build();
testService.createAccount(account);
}
}
Above Test is passing although the customer is not present in the database.
The test is incomplete. But still the statement: testService.createAccount(account);
must fail as per my understanding.
Kindly correct me if I am wrong. I am relatively new to Junit.
However if I place the implementation for checkAccountTypeForCustomer() inside the service method instead of in the helper, the test case fails as expected.
The reason is that accountHelper is mocked in your test, which means that invocation of accountHelper.checkAccountTypeForCustomer(account) doesn't execute your business code.
I recommend you to use Spring mocking in this case, and to specify how your repository is expected to behave. It would look something like this:
#ExtendWith(SpringExtension.class)
class AccountServiceTest {
#MockBean
private CustomerRepository repository;
#Autowired
private AccountService testService;
#Test
void testCreateAccount() throws CustomerNotFoundException {
Mockito.when(repository.existsById(anyInt())).thenReturn(false);
...
CustomerNotFoundException thrown = Assertions.assertThrows(CustomerNotFoundException.class, () -> testService.createAccount(account));
Assertions.assertEquals("the exception message", thrown.getMessage());
}
}
I've been using some previous examples of testing methods some of my work partners used and they didn't have any problem but when i use them for this project it doesn't work at all. One of my partners saw the methods and he didn't know either what was wrong.
This is my test class:
#Mock
UserRepository dataRepository;
#Autowired
protected UserService userService;
private User u;
#BeforeEach()
void setUp() {
u = new User();
u.setId(23L);
u.setUsername("test");
u.setPassword("Pass1234.");
u.setInfo(null);
this.dataRepository.save(u);
}
#Test
void testLoadUserByUsername() throws Exception {
when(dataRepository.findByUsername(anyString())).thenReturn(u);
userService.loadUserByUsername("test");
assertEquals(u, userService.loadUserByUsername(anyString()));
verify(dataRepository).findByUsername(anyString());
}
#Test
void testFindAllUsers() throws Exception {
List<User> user = this.userService.findAllUsers();
Assertions.assertNotNull(user);
}
}
When i execute the test i get the same trace every time and it is, for the first method:
org.springframework.security.core.userdetails.UsernameNotFoundException: User can't be found
at com.project.UserService.loadUserByUsername(UserService.java:41)
I don't know what could it be because i'm searching the same name i'm setting just a few lines above so if u could help me i would appreciate it.
The UserService class is:
#Autowired
private BCryptPasswordEncoder passw;
#Autowired
private UserRepository userRepository;
#Autowired
private DataRepository dataRepository;
#Override
public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException {
User user = this.userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("User can't be found");
}
return user;
}
public List<User> findAllUsers() {
List<User> l = new ArrayList<User>();
l = (List<User>) this.userRepository.findAll();
return l;
}
public void save(final User u) {
this.userRepository.save(u);
}
}
And the line of the code that is pointed by the exception is:
userService.loadUserByUsername("test");
Thank u so much.
the problem is with your #Mock. Basically, you are mocking an object in your test class and then pretending the #Autowired object in your UserService class to be mocked. What you are missing here is the instruction that tells Spring to put your mocked object into your UserService class. So here is the upgrade that will fix it.
Your test class becomes like this:
#Mock
UserRepository dataRepository;
#InjectMocks
#Autowired
protected UserService userService;
private User u;
#BeforeEach()
void setUp() {
u = new User();
u.setId(23L);
u.setUsername("test");
u.setPassword("Pass1234.");
u.setInfo(null);
this.dataRepository.save(u);
MockitoAnnotations.initMocks(this);
}
// Here goes the remaining code as it is...
Basically what I did was to add the #InjectMocks annotation above the #Autowired element that needs to receive your mock. Furthermore, with MockitoAnnotations.initMocks(this) I have initialized the mocks.
NOTE: the line where you do this.dataRepository.save(u); is useless. The point is that every method you call on a Mocked object is not executed, therefore there is no difference in having it or not. The only meaningful line is when(dataRepository.findByUsername(anyString())).thenReturn(u); that you have below.
PS. The dependency that contains #InjectMocks and MockitoAnnotations is the following:
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-all -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.8.4</version>
<scope>test</scope>
</dependency>
Wish you a great day,
D
I am having an issue with stubbing my repository. I was suggested to just create another application.properties (which I have not done) and to use an in-memory database like H2. I was wondering though if I can just stub the call so when myDataService.findById(id) is called instead of it attempting to get that from the database just a mocked object can be returned?
I am new to writing mocks for my unit tests and spring boot in general so maybe I am missing something. Code below (tried to simplify and made names generic for posting here).
My test class
public class MyServiceImplTest
{
private MyDataService myDataService;
private NyService myService;
private MyRepository myRepository;
#Before
public void setUp() {
myDataService = Mockito.mock(MyDataServiceImpl.class);
myService = new MyServiceImpl(myDataService);
}
#Test
public void getById_ValidId() {
doReturn(MyMockData.getMyObject()).when(myDataService).findById("1");
when(myService.getById("1")).thenReturn(MyMockData.getMyObject());
MyObject myObject = myService.getById("1");
//Whatever asserts need to be done on the object myObject
}
}
Class used for making the service call to the data layer
#Service
public class MyServiceImpl implements MyService {
MyDataService myDataService;
#Autowired
public MyServiceImpl(MyDataService myDataService) {
this.myDataService = myDataService;
}
#Override
public MyObject getById(String id) {
if(id == null || id == "") {
throw new InvalidRequestException("Invalid Identifier");
}
MyObject myObj;
try {
myObj = myDataService.findById(id);
}catch(Exception ex) {
throw new SystemException("Internal Server Error");
}
return myObj;
}
}
This is where I am having the issue in my test. When the findById() method is called, the variable repository is null so when trying to do repository.findOne(id) it throws an exceptionn. This is what I am attempting to mock, but the repository is giving me issues.
#Repository
#Qualifier("MyRepo")
public class MyDataServiceImpl {
#PersistenceContext
private EntityManager em;
private MyRepository repository;
#Autowired
public MyDataServiceImpl(MyRepository repository) {
super(repository);
this.repository = repository;
}
public MyObject findById(String id) {
P persitentObject = repository.findOne(id);
//Calls to map what persitentObject holds to MyObject and returns a MyObject
}
}
Code for MyRepository here just to show it's an empty interface that extends CrudRepository
public interface MyRepository extends CrudRepository<MyObjectPO, String>, JpaSpecificationExecutor<MyObjectPO> {
}
Let me begin by saying you are on the right track by using Constructor Injection and not Field Injection(which makes writing tests with mocks much simpler).
public class MyServiceImplTest
{
private MyDataService myDataService;
private NyService myService;
#Mock
private MyRepository myRepository;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this); // this is needed for inititalizytion of mocks, if you use #Mock
myDataService = new MyDataServiceImpl(myRepository);
myService = new MyServiceImpl(myDataService);
}
#Test
public void getById_ValidId() {
doReturn(someMockData).when(myRepository).findOne("1");
MyObject myObject = myService.getById("1");
//Whatever asserts need to be done on the object myObject
}
}
The call goes all the way from your service --> dataService. But only your repository calls are mocked.
This way you can control and test all the other parts of your classes(both service and dataService) and mock only repository calls.
My user class is as follows,
public class UserResource {
#Inject UserService userService;
public boolean createUser(User user) {
DbResponse res = userService.addUser(user);
if(res.isSuccess){
return true;
}else{
return false;
}
}
}
My test class looks as follows,
public class UserResourceTest {
UserResource userResource;
#BeforeMethod
void beforeMethod() {
userResource = new UserResource();
}
#Test
public void test() {
User user= mock(User.class);
boolean res= userResource.createUser(user);
assert(res);
}
}
As you can see a UserService object should be injected into the UserResource class. How can I inject a mock UserService object to userResource object inside my test?
FYI:
This is part of a Jersey JAX-RS project.
I'm using Java CDI, mockito and testNG (as the test library).
Consider using explicit dependency principal via constructor injection as it states very clearly what is required by the class in order to perform its particular function.
public class UserResource {
private UserService userService;
#Inject
public UserResource(UserService userService) {
this.userService = userService;
}
public boolean createUser(User user) {
DbResponse res = userService.addUser(user);
if(res.isSuccess){
return true;
}else{
return false;
}
}
}
and mock the UserService as well and assign it to the subject under test. Configure the desired/mocked behavior for the test.
public class UserResourceTest {
#Test
public void test() {
//Arrange
boolean expected = true;
DbResponse mockResponse = mock(DbResponse.class);
when(mockResponse.isSuccess).thenReturn(expected);
User user = mock(User.class);
UserService mockService = mock(UserService.class);
when(mockService.addUser(user)).thenReturn(mockResponse);
UserResource userResource = new UserResource(mockService);
//Act
boolean actual = userResource.createUser(user);
//Assert
assert(expected == actual);
}
}
Although I completely support the answer of #Nkosi I'd like to add this for completeness:
Use Mockitos JUnitRule to reate the mocks as described here: http://www.vogella.com/tutorials/Mockito/article.html :
public class UserResourceTest {
#Rule
public MockitoRule mockitoRule = MockitoJUnit.rule();
#Mock
private DbResponse mockResponse;
#Mock
private UserService mockService;
#Test
public void test() {
//Arrange
boolean expected = true;
when(mockResponse.isSuccess).thenReturn(expected);
when(mockService.addUser(user)).thenReturn(mockResponse);
// ...
Also then you could also use Mockitos #InjectMocks annotation like this:
public class UserResourceTest {
#Rule
public MockitoRule mockitoRule = MockitoJUnit.rule();
#Mock
private DbResponse mockResponse;
#Mock
private UserService mockService;
#InjectMocks
private UserResource userResource; // do not instantiate in test method
// ...
But I personally would discourage from it.
Yes, it is more convenient since it determines by reflection which dependency injection method you use. But if you don't have a "seam" to inject a certain dependency (neither Costructor parameter, non final property nor setter of matching type) you don't get a compile error which I personally find problematic.
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.