0
    when(mongoCollection.updateOne(new Document("_id", anyString()), new Document("$set", new Document("_id", anyString()).append("funds", anyInt())), any(UpdateOptions.class)))
        .then(i -> fundsByAccount.put(i.getArgument(0), i.getArgument(2)));

I have this java code i take in 4 matchers, but only 3 is expected, the first anyString() call and the second should be same, i want to reuse it so that i'll be able to do this in only 3 matchers call.

Code snippet and error

    when(mongoCollection.updateOne(new Document("_id", anyString()), new Document("$set", any(Document.class).append("funds", anyInt())), any(UpdateOptions.class)))
        .then(i -> fundsByAccount.put(i.getArgument(0), i.getArgument(2)));

I tried this but again this takes in 4 matcher calls, i need some way to reuse already captured values

  • You can't use argument Matchers like that. They can only be passed to the method you are mocking -- use `any(Document.class)` and get the strings out of the Document instance. – tgdavies Jan 15 '23 at 22:23
  • 1
    You need to think about how you want Mockito to decide whether the `when` is applicable, when a call to `updateOne` occurs. Is it enough to have `mongoCollection.updateOne(any(document.class), any(Document.class), any(UpdateOptions.class))`? If so, then write that. If not, you might have to use some more sophisticated matchers to detect whether the method was called with the right kinds of `Document`. – Dawood ibn Kareem Jan 15 '23 at 22:24
  • Mockito cannot "destructure" your arguments. Your method expects 3 arguments, so you have to use exactly 0 or 3 matchers (for the top-level arguments; you cannot supply arguments that are part real, part matchers). – knittl Jan 15 '23 at 22:36
  • You may find [Jeff Bowman's excellent self-answered question](https://stackoverflow.com/q/22822512) useful if you're trying to understand how to use matchers. – Dawood ibn Kareem Jan 16 '23 at 01:22
  • @tgdavies ok that helped thanks man appreciate that – Tanish azad Jan 16 '23 at 06:40

1 Answers1

1

Mockito cannot and will not "destructure" complex arguments for you. And it cannot match chained method calls (at least not in the way you expect). To set up stubs for a method call, you need to either provide 0 matchers or match exactly the number of formal parameters.

Your method has 3 parameters, so you need to pass 3 matchers:

when(mongoCollection.updateOne(any(Document.class), any(Document.class), any(Document.class)))
    .then(i -> fundsByAccount.put(i.getArgument(0), i.getArgument(2)));

If both calls are important, you can either create the mock with Answers.RETURNS_DEEP_STUBS (read the disclaimer in its JavaDoc!); or set up updateOne to return another mock:

final Appender appendMock = Mockito.mock(Appender.class); // I don't know your type, use the correct one
when(mongoCollection.updateOne(any(Document.class), any(Document.class), any(Document.class)))
        .thenReturn(appendMock);
when(appendMock.append("funds", anyInt()), any(UpdateOptions.class)))
        .thenAnswer(....);

If you need your method call to be only matched when it is called with specific instances, you can use the Mockito#argThat matcher:

Mockito.argThat(doc -> Objects.equals(doc.getKey(), "_id")),
Mockito.argThat(doc -> Objects.equals(doc.getKey(), "$set")
        && Objects.equals(doc.getValue().getKey(), "funds"));

But maybe you have less trouble if you implement the interface of mongoCollection in your own custom class, which can then hold the state in memory. This might be easier, especially if you mock this class in many different tests.

knittl
  • 246,190
  • 53
  • 318
  • 364