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

标签 java unit-testing groovy mockito spock

我有一个 Spring 接口(interface)的实现 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是一个标准接口(interface)扩展JpaRepository<UserEntity, Long>哪里UserEntity是我的模型类。

UserUserDetails 的实现来自 Spring 框架。

我使用 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: 节中添加来验证 userRepository 的调用时声明1 * userRepository.findByUsername(user.username)1 * userRepository.findByUsername(_ as String)我收到错误 UserDetailsServiceSpec.should find user and return new User:36 » UsernameNotFound 。第 36 行位于 when: 部分

最佳答案

您需要一步完成 stub 和验证

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

详细信息是我在 Predefined mock response in Spock 的回答:

请引用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

关于java - 我无法将 Mockito 单元测试重写为 Spock 规范,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46024769/

相关文章:

java - 如何在 javac/java 类路径中添加多个 .jar 文件 - 适用于 Debian Linux

java - 从Service类中的BaseAdapter获取数据

xml - 用于数据驱动单元测试的嵌套 XML

java - org.hibernate.MappingException 在 Grails v3.2.9 中使用 tablePerConcreteClass 继承策略时

java - Hibernate 找不到书来创建表

sql-server - 了解 SQL Server 查询性能测试 - 输出无法理解

c# - "Method ...ClassInitialize has wrong signature ..."是什么意思?

dictionary - groovy 中的 bool 方法总是返回 true

Groovy - 从字符串中提取并显示子字符串

java - 将 float 转换为 BigDecimal 然后再转换回 float