我有一个 Spring 接口(interface)的实现 UserDetailsService
:
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
private final UserRepository userRepository;
@Autowired
public UserDetailsServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
final UserEntity user = userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("Cannot find user with username " + username);
}
return new User(user);
}
}
UserRepository
是一个标准接口(interface)扩展JpaRepository<UserEntity, Long>
哪里UserEntity
是我的模型类。
User
是 UserDetails
的实现来自 Spring 框架。
我使用 JUnit 和 Mockito 为这个方法编写了一个单元测试。这些测试有效:
@RunWith(SpringRunner.class)
@DirtiesContext
public class UserDetailsServiceTest {
@Autowired
private UserDetailsService userDetailsService;
@Test
public void shouldFindUser() throws Exception {
// given
final UserEntity user = new UserEntity(
1L,
"username",
"username@email.com",
"password",
new ArrayList<>() // list of roles
);
when(UserDetailsServiceTestContext.userRepository.findByUsername(user.getUsername()))
.thenReturn(user);
// when
final UserDetails result = userDetailsService.loadUserByUsername(user.getUsername());
// then
assertThat(result).isEqualTo(UserFactory.create(user));
verify(UserDetailsServiceTestContext.userRepository)
.findByUsername(user.getUsername());
}
@Test(expected = UsernameNotFoundException.class)
public void shouldNotFindUser() throws Exception {
// given
when(UserDetailsServiceTestContext.userRepository.findByUsername(anyString()))
.thenReturn(null);
// when
final UserDetails result = userDetailsService.loadUserByUsername(new String());
}
@TestConfiguration
static class UserDetailsServiceTestContext {
@MockBean
private static UserRepository userRepository;
@Bean
UserDetailsService userDetailsService() {
return new UserDetailsServiceImpl(userRepository);
}
}
}
现在我尝试使用 Groovy 和 Spock 框架编写这些测试。我写了以下规范:
def 'should find user'() {
given:
def user = new UserEntity(
1L,
"username",
"username@email.com",
"password"
new ArrayList<>() // list of roles
)
userRepository.findByUsername(user.username) >> user
// userRepository.findByUsername(_ as String) >> user // also working
when:
def result = userDetailsService.loadUserByUsername(user.username)
then:
result == new User(user)
}
并且此测试正在运行。但是当我想通过在 then:
节中添加来验证 userRepository 的调用时声明1 * userRepository.findByUsername(user.username)
或1 * userRepository.findByUsername(_ as String)
我收到错误 UserDetailsServiceSpec.should find user and return new User:36 » UsernameNotFound
。第 36 行位于 when:
部分
最佳答案
您需要一步完成 stub 和验证
then:
1 * userRepository.findByUsername(user.username) >> user
详细信息是我在 Predefined mock response in Spock 的回答:
When mocking and stubbing the same method call, they have to happen in the same interaction. In particular, the following Mockito-style splitting of stubbing and mocking into two separate statements will not work:
setup:
subscriber.receive("message1") >> "ok"
when:
publisher.send("message1")
then:
1 * subscriber.receive("message1")
<小时/>As explained in Where to Declare Interactions, the receive call will first get matched against the interaction in the then: block. Since that interaction doesn’t specify a response, the default value for the method’s return type (null in this case) will be returned. (This is just another facet of Spock’s lenient approach to mocking.). Hence, the interaction in the setup: block will never get a chance to match.
在处理 spring 和事务代理时,您也可能会遇到这个问题 https://github.com/spockframework/spock/issues/758
关于java - 我无法将 Mockito 单元测试重写为 Spock 规范,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46024769/