Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When should we use @InjectMocks?

I have read through a lot of discussion about @Mock & @InjectMocks and still could not find what are the suitable or necessary cases to use @InjectMocks. In fact, I am not sure about what would happen when we use @InjectMocks.

Consider the following example,

public class User {
  private String user_name;
  private int user_id;
  public User(int user_id) {
    this.user_id = user_id;
  }
  public String getUser_name() {
    return user_name;
  }
  public void setUser_name(String user_name) {
    this.user_name = user_name;
  }
  public int getUser_id() {
    return user_id;
  }
  public void setUser_id(int user_id) {
    this.user_id = user_id;
  }     
}

public interface UserDao {
  public List<User> getUserList();
}

public class UserService {
  private UserDao userDao;  
  public UserDao getUserDao() {
    return userDao;
  }
  public void setUserDao(UserDao userDao) {
    this.userDao = userDao;
  }
  public List<User> getUserList() {
    return userDao.getUserList();
  }
}

This is my test class

@RunWith(MockitoJUnitRunner.class)
public class UserServiceTest {  
  private UserService service = new UserService();
  @Mock
  private UserDao dao;

  @Test
  public void testGetUserList() {

    service.setUserDao(dao);

    // mock the return value of the mock object
    List<User> mockResult = Arrays.asList(new User(101),new User(102),new User(103));       
    when(dao.getUserList()).thenReturn(mockResult);

    // check return value is same as mocked value
    assertEquals(service.getUserList(),mockResult);

    // verify the getUserList() function is called
    verify(dao).getUserList();  
  }
}    

The test run successfully with no errors. Consider another approach using @InjectMock annotation.

@RunWith(MockitoJUnitRunner.class)
public class UserServiceTest {  

  @InjectMocks
  private UserService service;
  @Mock
  private UserDao dao;

  @Test
  public void testGetUserList() {

    service.setUserDao(dao);

    // mock the return value of the mock object
    List<User> mockResult = Arrays.asList(new User(101),new User(102),new User(103));       
    when(dao.getUserList()).thenReturn(mockResult);

    // check return value is same as mocked value
    assertEquals(service.getUserList(),mockResult);

    // verify the getUserList() function is called
    verify(dao).getUserList();  
  }
}    

This works equally well. So, which way is better? Any best practice? Btw, I am using Junit 4.8.1 and Mockito 1.9.5

like image 660
Frankie Hung Avatar asked Apr 18 '17 04:04

Frankie Hung


People also ask

What is the use of InjectMocks?

@InjectMocks is the Mockito Annotation. It allows you to mark a field on which an injection is to be performed. Injection allows you to, Enable shorthand mock and spy injections.

Should you use InjectMocks?

You shouldn't use InjectMocks to deal with injecting private fields (err..or at all) , because this kind of Dependency Injection is evil – and signals you should change your design.

What is the difference between Mockbean and InjectMocks?

@Mock is used to declare/mock the references of the dependent beans, while @InjectMocks is used to mock the bean for which test is being created.

Can we use InjectMocks and spy together?

Issue 489 in mockito: @InjectMocks and @Spy cannot be used together when object initialized by mockito.

Can we use InjectMocks and Autowired together?

Here if you see @Autowired @InjectMocks are used together and what it will do is inject the mocked class (which is SomeRepository in our case) and Autowired annotation adds any other dependency which the class might have. The last thing you need to do is create a Setter for SomeRepository object in your actual class.

Can we mock InjectMocks?

Mockito @InjectMocks annotations allow us to inject mocked dependencies in the annotated class mocked object. This is useful when we have external dependencies in the class we want to mock. We can specify the mock objects to be injected using @Mock or @Spy annotations.


1 Answers

Your actual question can't be answered without you providing further code (the code you are showing does not explain the results you claim to observe).

Regarding the underlying question: you probably should not use @InjectMocks; one of its core problems is: if injecting fails, Mockito does not report an error to you.

In other words: you have passing unit tests, and some internal detail about a field changes ... and your unit tests break; but you have no idea why ... because the mocking framework doesn't tell you that the "initial" step of pushing a mock into the class under test is all of a sudden failing. See here for further reading.

But to be clear here: in the end, this almost a pure style question. People used to @InjectMocks will love it; other people do object it. Meaning: there is no clear evidence whether you should use this concept. Instead: you study the concept, you understand the concept, and then you (and the team working with you) make a conscious decision whether you want to use this annotation, or not.

Edit: I think I get your problem now. The idea of @InjectMocks is to inject a mocked object into some object under test.

But: you are doing that manually in both cases:

 service.setUserDao(dao);

Meaning: if injecting works correctly (and there isn't a problem that isn't reported by Mockito) then your example that uses that annotation should also work when you remove that one line. Whereas the testcase that doesn't have @InjectMocks should fail without that line!

In other words: your testcases are both passing because your code does a "manual inject"!

like image 109
GhostCat Avatar answered Sep 20 '22 17:09

GhostCat