【问题标题】:Mocking raw String in unit testing for Spring Boot service layer在 Spring Boot 服务层的单元测试中模拟原始字符串
【发布时间】:2020-06-03 23:45:00
【问题描述】:

我正在尝试为按名称查找Players 的服务层方法编写单元测试。该方法调用 JPA 存储库方法并返回一个 Page 对象。 我希望测试验证是否确实调用了存储库中的正确方法。

测试类

@RunWith(SpringRunner.class)
@SpringBootTest(classes = {PlayerService.class})
public class PlayerServiceTest {

    @Autowired
    PlayerService playerService;

    @MockBean
    PlayerRepository playerRepository;


    @Test
    public void whenListPlayersByName_thenShouldCallFindMethodWithPageableArgAndNameArg(){
        Pageable pageableStub = Mockito.mock(Pageable.class);
        String name = "xxx";
        Mockito.when(playerRepository.findByNameContainingIgnoreCase(any(String.class), any(Pageable.class)))
                .thenReturn(any(Page.class));

        //1st attempt:
        //playerService.listPlayersByName(name, pageableStub);
        playerService.listPlayersByName(eq(name), pageableStub);

        verify(playerRepository).findByNameContainingIgnoreCase(any(String.class), any(Pageable.class));
    }

我的问题

测试失败并显示一条消息:

org.mockito.exceptions.misusing.InvalidUseOfMatchersException: 
Invalid use of argument matchers!
2 matchers expected, 1 recorded:
-> at com.domin0x.player.PlayerServiceTest.whenListPlayersByName_thenShouldCallFindMethodWithPageableArgAndNameArg(PlayerServiceTest.java:60)

This exception may occur if matchers are combined with raw values:
    //incorrect:
    someMethod(anyObject(), "raw String");
When using matchers, all arguments have to be provided by matchers.
For example:
    //correct:
    someMethod(anyObject(), eq("String by matcher"));

按照建议,我将name 更改为eq(name),但这会导致不同的问题:

Argument(s) are different! Wanted:
com.domin0x.player.PlayerRepository#0 bean.findByNameContainingIgnoreCase(
    <any java.lang.String>, <any org.springframework.data.domain.Pageable>);

Actual invocation has different arguments:
com.domin0x.player.PlayerRepository#0 bean.findByNameContainingIgnoreCase(
null,     Mock for Pageable, hashCode: 309271464
;

有什么建议我应该在测试中改变什么?

服务类

@Service
public class PlayerService {
    public Page<Player> listPlayersByName(String name, Pageable pageable) {
        return repository.findByNameContainingIgnoreCase(name, pageable);
    }

存储库界面

@Repository
public interface PlayerRepository extends JpaRepository<Player, Integer> {

    Page<Player> findByNameContainingIgnoreCase(String name, Pageable pageable);
}

【问题讨论】:

  • 我没有看到 eq() 在任何地方使用。我看到any() 用了几个地方。您确定您共享的代码与正在使用的代码相同吗?
  • 我想我找到了你的问题,添加了答案。
  • 我在没有使用 eq() 的情况下发布了 playerService.listPlayersByName 电话,因为那是我最初的尝试。我现在编辑了测试方法代码以使其更清晰。
  • 你在下面看到我的回答了吗?

标签: java spring junit mockito


【解决方案1】:

我花了一段时间才弄明白。

thenReturn 中,您正在呼叫any(Page.class)。相反,您应该返回一个实际的Page 对象,或者一个模拟的Page 对象)。

除非您无法知道身份,否则最好避免使用“any”。

Page<Player> pageStub = (Page<Player>)Mockito.mock(Page.class);
Mockito.when(playerRepository.findByNameContainingIgnoreCase(name, pageableStub))
            .thenReturn(pageStub);

Page<PlayerStub> result = playerService.listPlayersByName(name, pageableStub);

assertSame(pageStub, result);

// No need to call verify, since it couldn't get pageStub without calling the correctly stubbed method.

澄清一下:eq()any() 和其他“匹配器”只能用作whenverify 中方法的参数。它们不应该传递给测试对象,也不应该从任何模拟对象返回。

【讨论】:

  • 我接受了您的回答,但请将解决方案中的第一行更改为 Page&lt;Player&gt; pageStub = Mockito.mock(Page.class);(或类似名称)。到目前为止,由于拼写错误,该代码无法编译:您声明了playerStub,但使用了pageStub。我使用的 Page 类也来自 org.springframework.data.domain.Page,这是抽象的,但这是我的问题,因为我没有在我的问题中指定它。
  • 我更新了我的答案。 FWIW,您可以自己进行编辑。
猜你喜欢
  • 2020-02-10
  • 1970-01-01
  • 1970-01-01
  • 2021-09-23
  • 2013-03-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多