I am trying to write unit tests for a service layer method finding Player
s by name. The method calls a JPA repository method and returns a Page object. I want the test to verify that the correct method from repository was indeed called.
The test class
@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));
}
My problem
The test fails with a message:
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"));
Following the advice I changed the name
to eq(name)
but that results in a different issue:
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
;
Any advice what should I change in the test?
The service class
@Service
public class PlayerService {
public Page<Player> listPlayersByName(String name, Pageable pageable) {
return repository.findByNameContainingIgnoreCase(name, pageable);
}
The repository interface
@Repository
public interface PlayerRepository extends JpaRepository<Player, Integer> {
Page<Player> findByNameContainingIgnoreCase(String name, Pageable pageable);
}
It took me a while to figure this one out.
in thenReturn
, you are calling any(Page.class)
. Instead, you should return either an actual Page
object, or a mock Page
object).
It's also better to avoid using "any" unless you have no way to know the identity.
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.
To clarify: eq()
, any()
, and other "matchers" should only be used as arguments to methods in when
and verify
. They should never be passed to the test subject, nor returned from any mocked object.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.