简体   繁体   English

我无法将Mockito单元测试重写为Spock规范

[英]I cannot rewrite Mockito unit test to Spock Specification

I have an implementation of Spring interface UserDetailsService : 我有一个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 is a standard interface extendng JpaRepository<UserEntity, Long> where UserEntity is my model class. UserRepositoryJpaRepository<UserEntity, Long>的标准接口JpaRepository<UserEntity, Long>其中UserEntity是我的模型类。

User is an implementation of UserDetails from Spring Framework. User是Spring Framework中UserDetails的实现。

And I wrote an unit test for this method using JUnit and Mockito. 我使用JUnit和Mockito为此方法编写了一个单元测试。 These tests are working: 这些测试有效:

@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);
        }
    }

}

And now I try to write these tests using Groovy and Spock framework. 现在,我尝试使用Groovy和Spock框架编写这些测试。 I wrote the following specification: 我写了以下规范:

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)
    }

and this test is working. 并且此测试有效。 But when I want verify calling of userRepository by add in section then: a statement 1 * userRepository.findByUsername(user.username) or 1 * userRepository.findByUsername(_ as String) I get an error UserDetailsServiceSpec.should find user and return new User:36 » UsernameNotFound . 但是,当我想通过在本节中添加来验证对userRepository的调用时then:一条语句1 * userRepository.findByUsername(user.username)1 * userRepository.findByUsername(_ as String)我得到一个错误UserDetailsServiceSpec.should find user and return new User:36 » UsernameNotFound line 36 is in section when: 第36行位于以下when:

You need to do the stubbing and verification in one step 您需要一步完成存根和验证

then:
1 * userRepository.findByUsername(user.username) >> user

For details here is my answer from Predefined mock response in Spock : 有关详细信息,这是我在Spock中的预定义模拟响应中的答案:

Please refer to the 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: 特别是,以下将Mockito样式的存根和模拟拆分为两个单独的语句将不起作用:

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. 如在哪里声明交互中所述,接收调用将首先与then:块中的交互匹配。 Since that interaction doesn't specify a response, the default value for the method's return type (null in this case) will be returned. 由于该交互未指定响应,因此将返回该方法的返回类型的默认值(在这种情况下为null)。 (This is just another facet of Spock's lenient approach to mocking.). (这只是Spock宽大的嘲笑方法的另一个方面。) Hence, the interaction in the setup: block will never get a chance to match. 因此,setup:块中的交互将永远不会有匹配的机会。


When dealing with spring and transaction proxies you might also run into this problem https://github.com/spockframework/spock/issues/758 在处理spring和transaction代理时,您可能还会遇到此问题https://github.com/spockframework/spock/issues/758

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM