Spring Boot Mockito - @InjectMocks - 如何仅模拟选定的依赖项
Spring Boot Mockito - @InjectMocks - How to mock selected dependencies only
我有一个名为 UserServiceImpl
的 @Service
,它依赖于另外两个 bean。一个是 UserRepository
bean,另一个是名为 SessionService
.
的 bean
我的要求是在测试 UserServiceImpl class 期间,我必须能够注入 SessionService
依赖项的模拟,但保持 UserRepository
依赖项原样。
我的服务 class 看起来像这样:
@Service
@Slf4j
public class UserServiceImpl implements UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private SessionService sessionService;
@Override
public User create(User user) {
log.info("User Creation at Service");
// ... Do some validations .. //
// This needs to be mocked in Unit Tests
String returnValue = sessionService.doSomethingThatIDontWantInTests();
user.setInternalKey(returnValue);
// .. Do some more Validations .. //
return userRepository.save(user);
}
}
现在,这是我的测试 Class:
@SpringBootTest
class UserServiceTest {
@InjectMocks
private UserServiceImpl userService;
@Mock
private SessionService sessionService;
@Test
void CreateUserTest() {
Mockito.when(sessionService.doSomethingThatIDontWantInTests()).thenReturn("abcxyz123321");
User user = new User();
user.setName("John Doe");
user.setEmail("john.doe@example.com");
User savedUser = userService.create(user);
assertNotNull(savedUser.getUserId());
}
}
当我 运行 这个测试时,Mockito 成功模拟了 SessionService
调用。但是,UserServiceImpl.createUser()
仍然失败并显示消息:
java.lang.NullPointerException: Cannot invoke "com.myproject.data.repos.UserRepository.save(User)" because "this.userRepository" is null
我是否也应该将 UserRepository
作为模拟注入并使用 Mockito 模拟 UserRepository.save()
方法?
我只想模拟 SessionService
依赖项,而不是 UserRepository
依赖项。
这是可行的吗?如果是这样,如何?请指教
谢谢,
斯里拉姆
您正在混合不同的测试风格。
样式 1 - spring 集成测试
此时 Spring Boot 在其上下文中创建 bean,然后将它们注入到测试中 class。
- 使用
@SpringBootTest
- 使用
@Autowired
将 bean 注入到您的测试中
- 使用
@MockBean
将 Spring 上下文中的 bean 替换为 mocks
样式 2 - 单元测试
这不使用 Spring DI。在这种风格中,通常会模拟所有依赖项。
- 使用
@ExtendWith(MockitoExtension.class)
- 将依赖项注释为
@Mock
- 用
@InjectMocks
注释 SUT
也可以使用真正的依赖项,但在那种情况下您需要手动构建 SUT - Mockito 不支持部分注入。
单元测试往往更轻量且不那么脆弱,另一方面,集成测试覆盖了应用程序的大部分内容。
请注意,如果 UserRepository
是 spring-data repo,则无法手动创建。
您必须使用 @SpyBean
(https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/mock/mockito/SpyBean.html),它将注入原始 bean,但您可以验证调用和参数。
我有一个名为 UserServiceImpl
的 @Service
,它依赖于另外两个 bean。一个是 UserRepository
bean,另一个是名为 SessionService
.
我的要求是在测试 UserServiceImpl class 期间,我必须能够注入 SessionService
依赖项的模拟,但保持 UserRepository
依赖项原样。
我的服务 class 看起来像这样:
@Service
@Slf4j
public class UserServiceImpl implements UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private SessionService sessionService;
@Override
public User create(User user) {
log.info("User Creation at Service");
// ... Do some validations .. //
// This needs to be mocked in Unit Tests
String returnValue = sessionService.doSomethingThatIDontWantInTests();
user.setInternalKey(returnValue);
// .. Do some more Validations .. //
return userRepository.save(user);
}
}
现在,这是我的测试 Class:
@SpringBootTest
class UserServiceTest {
@InjectMocks
private UserServiceImpl userService;
@Mock
private SessionService sessionService;
@Test
void CreateUserTest() {
Mockito.when(sessionService.doSomethingThatIDontWantInTests()).thenReturn("abcxyz123321");
User user = new User();
user.setName("John Doe");
user.setEmail("john.doe@example.com");
User savedUser = userService.create(user);
assertNotNull(savedUser.getUserId());
}
}
当我 运行 这个测试时,Mockito 成功模拟了 SessionService
调用。但是,UserServiceImpl.createUser()
仍然失败并显示消息:
java.lang.NullPointerException: Cannot invoke "com.myproject.data.repos.UserRepository.save(User)" because "this.userRepository" is null
我是否也应该将 UserRepository
作为模拟注入并使用 Mockito 模拟 UserRepository.save()
方法?
我只想模拟 SessionService
依赖项,而不是 UserRepository
依赖项。
这是可行的吗?如果是这样,如何?请指教
谢谢, 斯里拉姆
您正在混合不同的测试风格。
样式 1 - spring 集成测试
此时 Spring Boot 在其上下文中创建 bean,然后将它们注入到测试中 class。
- 使用
@SpringBootTest
- 使用
@Autowired
将 bean 注入到您的测试中 - 使用
@MockBean
将 Spring 上下文中的 bean 替换为 mocks
样式 2 - 单元测试
这不使用 Spring DI。在这种风格中,通常会模拟所有依赖项。
- 使用
@ExtendWith(MockitoExtension.class)
- 将依赖项注释为
@Mock
- 用
@InjectMocks
注释 SUT
也可以使用真正的依赖项,但在那种情况下您需要手动构建 SUT - Mockito 不支持部分注入。
单元测试往往更轻量且不那么脆弱,另一方面,集成测试覆盖了应用程序的大部分内容。
请注意,如果 UserRepository
是 spring-data repo,则无法手动创建。
您必须使用 @SpyBean
(https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/mock/mockito/SpyBean.html),它将注入原始 bean,但您可以验证调用和参数。