我不能改写的Mockito单元测试斯波克规格

我不能改写的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我的回答:

请参阅documentation

当模拟和存根相同的方法调用时,它们必须发生在相同的交互中。特别是,磕碰和嘲笑成两个独立的语句下面的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