我无法将 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

有关详细信息,请参阅我在 中的回答:

请参考documentation

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