我不能改写的Mockito单元测试斯波克规格
我有春天接口UserDetailsService
的实现:我不能改写的Mockito单元测试斯波克规格
@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
是一个标准的接口extendng JpaRepository<UserEntity, Long>
其中UserEntity
是我的模型类。
User
是来自Spring Framework的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",
"[email protected]",
"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",
"[email protected]",
"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
对于这里的细节是Predefined mock response in Spock我的回答:
当模拟和存根相同的方法调用时,它们必须发生在相同的交互中。特别是,磕碰和嘲笑成两个独立的语句下面的Mockito式的分裂将无法正常工作:
setup:
subscriber.receive("message1") >> "ok"
when:
publisher.send("message1")
then:
1 * subscriber.receive("message1")
正如在哪里声明互动解释,接收电话将首先获得对互动匹配在then:块中。由于该交互没有指定响应,因此将返回方法返回类型的默认值(在这种情况下为null)。 (这只是Spock宽松处理嘲笑的另一个方面)。因此,setup:block中的交互永远不会有匹配的机会。
当春天的交易和交易代理,你也可能碰到这个问题https://github.com/spockframework/spock/issues/758