当使用 lombok @RequiredArgsConstructor(onConstructor = @__(@Autowired)) 时,Mockito when().thenReturn() 的行为不符合预期
Mockito when().thenReturn() does not behave as expected when lombok @RequiredArgsConstructor(onConstructor = @__(@Autowired)) is used
我有 Spring class 说
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
class MainServiceImpl implements MainService {
private final InternalService internalService;
public Set<String> do(String anything) {
Set<String> relevent = internalService.finaIntern(anything);
return relevent;
}
}
我正在编写如下单元测试用例
@RunWith(MockitoJUnitRunner.class)
class TestMainServiceImpl {
@InjectMocks
private MainServiceImpl service;
@Mock
InternalService internalService;
@Before
public void init(){
MockitoAnnotations.initMocks(this);
}
@Test
public void testDo() {
Set<String> setData = new HashSet<>();
setData.add("ABC");
String a ="a";
when(internalService.finaIntern(any(String.class))
.thenReturn(setData);
Set<String> result = service.do(a);
assertTrue(!result.isEmpty());
}
}
这里我的测试用例失败了,但是如果我从 MainServiceImpl 中删除 final 并执行如下所示的显式 @Autowired
@Component
class MainServiceImpl implements MainService {
@Autowired
private InternalService internalService;
.....
在这里我很想知道
1.如果我删除final关键字,我的测试用例如何通过
2. 使用 @RequiredArgsConstructor 是一种好习惯吗?如果是,那么如何使用?如果不是,那么为什么?
提前致谢
与lombok
和Spring@Autowired
无关
@RunWith(MockitoJUnitRunner.class)
和 MockitoAnnotations.initMocks(this);
的组合就是问题所在。删除其中任何一个,行为就如预期的那样。你不需要他们两个。事实上 MockitoAnnotations.initMocks(this);
只存在于你不能使用 @RunWith(MockitoJUnitRunner.class)
的情况下,例如如果你需要使用 SpringRunner.class
.
这就是它不起作用的原因。
首先实例化所有对象。所以你的 @Mock
被创建并注入你的 @InjectMock
object:
下面可以看到,injectInto
和mock
里面的new create mock(mocks[0]
),service
是同一个对象
但是第二次初始化发生了。
因此 mockito 创建一个新的 @Mock
对象,并尝试将其注入到已经实例化的 @InjectMock
对象中。但是只要它是最终的,就无法将它注入到该领域。
所以这是我们在第二次初始化后得到的:
如您所见,现在 testClassInstance
中的模拟对象与注入到被测对象的模拟对象不同。
那@RequiredArgsConstructor
呢:对我来说,按照你的方式使用它是完全可以的。
你的测试用例应该是这样的。你不应该使用
MockitoAnnotations.initMocks(this)
当您将 @InjectMock 用于被测 class 时。这适用于@AllArgConstructor。
@RunWith(MockitoJUnitRunner.class)
class TestMainServiceImpl {
@InjectMocks
private MainServiceImpl service;
@Mock
InternalService internalService;
@Test
public void testDo() {
Set<String> setData = new HashSet<>();
setData.add("ABC");
String a ="a";
when(internalService.finaIntern(any(String.class))
.thenReturn(setData);
Set<String> result = service.do(a);
assertTrue(!result.isEmpty());
}
}
试试上面的方法为@allArgConstructor 和@RequiredArgConstructor 编写测试用例。如果这个剂量有效,你可以试试下面的逻辑。
如果您在 class 中仅使用 @Data 注释 (lombok.Data),则将生成仅具有必填(最终)字段的构造函数。这是因为 Data 隐含了 RequiredArgsConstructor。示例:
@Data
@Component
class MainServiceImpl implements MainService {
private final InternalService internalService;
}
注解@Builder 为我们生成 MainServiceImpl class 帮助我们创建新的 MainServiceImpl
@Data
@Builder
@Component
class MainServiceImpl implements MainService {
private final InternalService internalService;
}
我有 Spring class 说
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
class MainServiceImpl implements MainService {
private final InternalService internalService;
public Set<String> do(String anything) {
Set<String> relevent = internalService.finaIntern(anything);
return relevent;
}
}
我正在编写如下单元测试用例
@RunWith(MockitoJUnitRunner.class)
class TestMainServiceImpl {
@InjectMocks
private MainServiceImpl service;
@Mock
InternalService internalService;
@Before
public void init(){
MockitoAnnotations.initMocks(this);
}
@Test
public void testDo() {
Set<String> setData = new HashSet<>();
setData.add("ABC");
String a ="a";
when(internalService.finaIntern(any(String.class))
.thenReturn(setData);
Set<String> result = service.do(a);
assertTrue(!result.isEmpty());
}
}
这里我的测试用例失败了,但是如果我从 MainServiceImpl 中删除 final 并执行如下所示的显式 @Autowired
@Component
class MainServiceImpl implements MainService {
@Autowired
private InternalService internalService;
.....
在这里我很想知道 1.如果我删除final关键字,我的测试用例如何通过 2. 使用 @RequiredArgsConstructor 是一种好习惯吗?如果是,那么如何使用?如果不是,那么为什么?
提前致谢
与lombok
和Spring@Autowired
无关
@RunWith(MockitoJUnitRunner.class)
和 MockitoAnnotations.initMocks(this);
的组合就是问题所在。删除其中任何一个,行为就如预期的那样。你不需要他们两个。事实上 MockitoAnnotations.initMocks(this);
只存在于你不能使用 @RunWith(MockitoJUnitRunner.class)
的情况下,例如如果你需要使用 SpringRunner.class
.
这就是它不起作用的原因。
首先实例化所有对象。所以你的 @Mock
被创建并注入你的 @InjectMock
object:
下面可以看到,injectInto
和mock
里面的new create mock(mocks[0]
),service
是同一个对象
但是第二次初始化发生了。
因此 mockito 创建一个新的 @Mock
对象,并尝试将其注入到已经实例化的 @InjectMock
对象中。但是只要它是最终的,就无法将它注入到该领域。
所以这是我们在第二次初始化后得到的:
testClassInstance
中的模拟对象与注入到被测对象的模拟对象不同。
那@RequiredArgsConstructor
呢:对我来说,按照你的方式使用它是完全可以的。
你的测试用例应该是这样的。你不应该使用
MockitoAnnotations.initMocks(this)
当您将 @InjectMock 用于被测 class 时。这适用于@AllArgConstructor。
@RunWith(MockitoJUnitRunner.class)
class TestMainServiceImpl {
@InjectMocks
private MainServiceImpl service;
@Mock
InternalService internalService;
@Test
public void testDo() {
Set<String> setData = new HashSet<>();
setData.add("ABC");
String a ="a";
when(internalService.finaIntern(any(String.class))
.thenReturn(setData);
Set<String> result = service.do(a);
assertTrue(!result.isEmpty());
}
}
试试上面的方法为@allArgConstructor 和@RequiredArgConstructor 编写测试用例。如果这个剂量有效,你可以试试下面的逻辑。
如果您在 class 中仅使用 @Data 注释 (lombok.Data),则将生成仅具有必填(最终)字段的构造函数。这是因为 Data 隐含了 RequiredArgsConstructor。示例:
@Data
@Component
class MainServiceImpl implements MainService {
private final InternalService internalService;
}
注解@Builder 为我们生成 MainServiceImpl class 帮助我们创建新的 MainServiceImpl
@Data
@Builder
@Component
class MainServiceImpl implements MainService {
private final InternalService internalService;
}