我无法将 Mockito 单元测试重写为 Spock 规范
I cannot rewrite Mockito unit test to Spock Specification
我有一个 Spring 接口的实现 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
是标准接口扩展 JpaRepository<UserEntity, Long>
其中 UserEntity
是我的模型 class.
User
是来自 Spring 框架的 UserDetails
的实现。
并且我使用 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:
部分添加一条语句 1 * userRepository.findByUsername(user.username)
或 1 * userRepository.findByUsername(_ as String)
来验证对 userRepository 的调用时,我收到错误 UserDetailsServiceSpec.should find user and return new User:36 » UsernameNotFound
。第 36 行在 when:
部分
您需要一步完成存根和验证
then:
1 * userRepository.findByUsername(user.username) >> user
有关详细信息,请参阅我在 中的回答:
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
我有一个 Spring 接口的实现 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
是标准接口扩展 JpaRepository<UserEntity, Long>
其中 UserEntity
是我的模型 class.
User
是来自 Spring 框架的 UserDetails
的实现。
并且我使用 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:
部分添加一条语句 1 * userRepository.findByUsername(user.username)
或 1 * userRepository.findByUsername(_ as String)
来验证对 userRepository 的调用时,我收到错误 UserDetailsServiceSpec.should find user and return new User:36 » UsernameNotFound
。第 36 行在 when:
您需要一步完成存根和验证
then:
1 * userRepository.findByUsername(user.username) >> user
有关详细信息,请参阅我在
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