我已经阅读了很多关于@Mock& amp; @InjectMocks仍然无法找到使用@InjectMocks的合适或必要的案例。事实上,我不确定在使用@InjectMocks时会发生什么。
考虑以下示例,
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();
}
}
这是我的测试类
@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();
}
}
测试成功运行且没有错误。 考虑使用@InjectMock注释的另一种方法。
@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();
}
}
这同样有效。那么,哪种方式更好?任何最佳做法? 顺便说一句,我使用的是Junit 4.8.1和Mockito 1.9.5
答案 0 :(得分:5)
如果没有提供更多代码,您的实际问题无法得到解答(您展示的代码并未解释您声称要遵守的结果)。
关于基本问题:你可能不应该使用@InjectMocks;其中一个核心问题是:如果注入失败,Mockito会不向您报告错误。
换句话说:你已经通过了单元测试,并且有关字段的一些内部细节发生变化......并且你的单元测试中断了;但你不知道为什么......因为模拟框架并没有告诉你&#34; initial&#34;将模拟推入被测试阶段的步骤突然失败了。有关详细信息,请参阅here。
但要明确:最后,这几乎是一个纯粹的风格的问题。习惯于@InjectMocks的人会喜欢它;其他人反对它。含义:没有明确的证据表明你是否应该使用这个概念。相反:你研究这个概念,你理解这个概念,然后你(以及与你一起工作的团队)有意识地决定你是否想要来使用这个注释。
编辑:我想我现在得到了你的问题。 @InjectMocks的想法是将 模拟对象加入一些测试对象。
但是:在这两种情况下你都是手动:
service.setUserDao(dao);
含义:如果注入正常工作(并且没有Mockito报告的问题)那么使用该注释的示例应也删除那一行时工作。虽然没有@InjectMocks的测试用例失败而没有该行!
换句话说:您的测试用例都在通过,因为您的代码执行了#34;手动注入&#34;!
答案 1 :(得分:0)
使用@InjectMocks将模拟对象作为依赖项注入到创建的对象(由@InjectMocks标记的对象)。使用构造函数创建对象将违反目的,因为未注入模拟对象,因此来自SUT的所有外部调用都发生在具体对象上。尝试避免通过构造函数调用创建SUT对象。
答案 2 :(得分:0)
我们什么时候应该使用
@InjectMocks
?
IHMO:我们不应该使用它。
这支持了许多设计问题,因此我想在这个问题上添加我的POV。
Here is relevant information from the javadoc(重点不是我的):
标记应在其上进行注射的字段。
允许速记模拟和间谍注入。
最小化重复的模拟和间谍注入。
Mockito将尝试仅通过构造函数注入来注入模拟, 设置器注入或属性注入,顺序和描述如下 下面。如果以下任何策略失败,则Mockito 不会 报告失败;即您必须自己提供依赖项。 ... Mockito不是依赖注入框架,
用两个词表示:
优点:只需编写更少的代码即可设置模拟依赖项。
缺点:如果未在测试对象中设置某些模拟,则不会失败。
关于优势:
Mockito使用构造函数/设置器/反射来设置被测对象中的模拟对象。问题在于,由您决定设置字段的方式:mockito会尝试另一种方式。
setter方法通常非常冗长,并且提供了依赖项的可变性。因此,开发人员不经常使用它。很有道理
设置Bean依赖项的构造方法要求保持良好的设计并具有相当数量的依赖项:通常不超过4或5。
它需要强大的设计需求。
最后,反射方式是最容易的:只需编写很少的代码,而无需依赖项的可变性。
反射方式是lambda开发人员PVO中最简单的方法,我看到许多团队不了解反射的后果,因为它们不仅在Mockito测试中指定反射方式的依赖性和滥用,而且在设计中也是如此,因为最终InjectMocks
允许以某种方式应对它。
您可能会认为,最好不要使用将30个setters声明为依赖项的类或带有30个参数的构造函数来完成,但这也有一个很大的缺点:它倾向于产生不良气味,例如依赖项字段隐藏和作为班级。结果,您的班级复杂,膨胀,难以阅读,难以测试并且易于维护。
真正的现实是,在大多数情况下,正确的解决方案是构造函数注入,该注入可以促进体面的类责任,并因此具有良好的可维护性和可测试性。
关于缺点:
模拟注入不能作为IOC的依赖注入。
Javadoc声明自己:
Mockito不是依赖项注入框架,
IOC和DI框架能够并且默认情况下根据需要考虑依赖关系。因此,任何缺少的依赖关系都会在启动时激起fast-fail error。
它可以防止很多头痛,使您赢得宝贵的时间。
虽然Mockito注入将默默地忽略未设法注入的模拟。由您来猜测错误原因。
找到问题原因有时很复杂,因为您没有Mockito的任何线索,而所有事情都是Mockito的反思。