0

I have the following

public class SenderHeaders {
    public Map<String, Object> getHeaders(String subject) {
        Map<String, Object> headers = new HashMap<>();
        ...
        return headers;
    }
}

public class Sender {
  private SenderHeaders senderHeaders;

  public Sender(SenderHeaders senderHeaders) {
    this.senderHeaders = senderHeaders;
  }

  public void sendFirstAndSecondDocuments(ATask aTask, Optional<ATask> secondTask, String subject) {
    Map<String, Object> headers = senderHeaders.getHeaders(subject);
    sendDocument(new Document(aTask, headers));
    if (secondTask.isPresent()) {
        sendDocument(new Document(secondTask.get(), senderHeaders.getHeaders(subject)));
    }
  }


  public void sendDocument(Document<ATask> Document) {
        ...
  }
  ....
}


public class BSending {
    ...
    private final String subject;
    ...
    @Autowired
    public BSending(Sender sender, AMapper aMapper, String subject) {
        ...
        this.subject = subject;
    }

    public void sendTask(CTask cTask) {
        final var aTask = getATask(cTask);
        final var secondTask = getSecondTask(cTask);

        aSender.sendFirstAndSecondDocuments(aTask, secondTask, subject);
    }


}

public class BSendingTest {
    @Mock private Sender aSender;
    @Mock ATask aTask;
    @Mock ATask secondTask;
    @Mock Map<String, Object> headers;
    static final String subject = "subject";
    @Mock private SenderHeaders senderHeaders;
    @InjectMocks private BSending bSending;
    ...


    @Test
    void test() {
        ...
        when(senderHeaders.getHeaders(subject)).thenReturn(headers);

        doCallRealMethod().when(aSender).sendFirstAndSecondDocuments(aTask, Optional.empty(), subject);

        ArgumentCaptor<Document<ATask>> documentCaptor = ArgumentCaptor.forClass(Document.class);
        bSending.sendTask(cTask);

        verify(aSender, times(1)).sendFirstAndSecondDocuments(aEvent, Optional.empty(), subject);

        verify(aSender, times(1)).sendDocument(documentCaptor.capture());
        final var actual = documentCaptor.getValue();
        assertEquals(aTask, actual.get(0).getBody());
        ..
    }
}

I got this error

strict stubbing argument mismatch
this invocation of 'sendFirstAndSecondDocuments' method:
aSender.sendFirstAndSecondDocuments(
aEvent,
Optional.empty,
null)

has following stubbing(s) with different arguments:
aSender.sendFirstAndSecondDocuments(
aEvent,
Optional.empty,
"subject")

why is it being null when I passed in subject?

I also noticed when I was debugging and hit Map<String, Object> headers = senderHeaders.getHeaders(subject); inside of sendFirstAndSecondDocuments I noticed it said senderHeaders = 'this' is not available

when I replace subject with null for the doCallRealMethod and verify for sendFirstAndSecondDocuments I then get

nullPointerException: cannot invoke getHeaders(String) because <local5>.senderHeaders is null

I don't understand this because I already mocked senderHeaders and have when(senderHeaders.getHeaders(subject)).thenReturn(headers);

Can I get help with this?

EDIT: Modified getHeaders() so its public but I still get the same errors

EDIT2: I created another code that is hopefully easier to understand but gives similar error:

public class SenderPrint {
    public void printTask(ATask aTask) {
        System.out.println("printLetters: " + aTask.getLetters());
    }

    public void confirmTask(ATask aTask) {
        System.out.println("confirmLetters: " + aTask.getLetters());
    }
}

public class Sender {
    private SenderPrint senderPrint;

    public Sender(SenderPrint senderPrint) {
        this.senderPrint = senderPrint;
    }

    public void printSenderLetters(ATask aTask) {
        senderPrint.printTask(aTask);
        senderPrint.confirmTask(aTask);
        if (aTask.getLetters() == "subject") {
            senderPrint.printTask(aTask);
        }
    }
}

public class ATask {
    private String letters;
    public String getLetters() {
        return this.letters;
    }
    public void setLetters(String letters) {
        this.letters = letters;
    }
}


public class BSending {
    protected final Sender sender;

    public BSending(Sender sender) {
        this.sender = sender;
    }

    public void sendTask(ATask aTask) {
        sender.printSenderLetters(aTask);
    }

}

public class BSendingTest {
    @Mock private Sender sender;
    @Mock ATask aTask;
    @Mock private senderPrint senderPrint;


    @Test
    void test() {
        BSending bSending = new BSending(sender);
        doCallRealMethod().when(senderPrint).printTask(aTask);
        doCallRealMethod().when(senderPrint).confirmTask(aTask);
        bSending.sendTask(aTask);
        verify(sender, times(1)).printSenderLetters(aTask);
        verify(senderPrint, times(1)).printTask(aTask);
        verify(senderPrint, times(1)).confirmTask(aTask);
        ArgumentCaptor<ATask> captor = ArgumentCaptor.forClass(ATask.class);
        final var actual = captor.getValue();
    }
}

Gives

wanted but not invoked
senderPrint.printTask(aTask)
actually there were zero interactions with this mock
user5739619
  • 1,748
  • 5
  • 26
  • 40
  • the method senderHeaders.getHeaders(subject) is private. You can not mock it with mockito. If you want to mock it then use reflection or powermockito. Another option is to change private to protected or don't mock it and pass the real value which will not cause an exception(use subject which will pass all getHeaders() method). – Feel free May 24 '23 at 20:07
  • that was a typo. Even when I set it as public, I get the same errors – user5739619 May 24 '23 at 21:44
  • 1
    I don't see where you create your `BSending` instance. Please create a minimal, runnable example which reproduces the problem. – tgdavies May 25 '23 at 02:35
  • I edited the `BSendingTest` to include it – user5739619 May 25 '23 at 03:06
  • 2
    I wouldn't expect `@InjectMocks` to pass `subject` to the constructor -- try creating your `BSending` instance explicitly. – tgdavies May 25 '23 at 03:20
  • I get the same error even creating the instance explicitly. I created another code that is hopefully easier to understand but gives similar error: https://pastebin.com/WmEMztaT – user5739619 May 25 '23 at 03:34
  • Please create a minimal, *complete*, *runnable* example which reproduces the problem. – tgdavies May 25 '23 at 03:44
  • I updated the pastebin so its minimal complete and runnable – user5739619 May 25 '23 at 05:12
  • 1
    That paste is 403 for me. And your code should be in your question, anyway. – tgdavies May 25 '23 at 05:26
  • I pasted the updated code in EDIT2 – user5739619 May 25 '23 at 05:37
  • 1
    See https://stackoverflow.com/questions/513832/how-do-i-compare-strings-in-java/513839#513839 – tgdavies May 25 '23 at 05:42
  • `@InjectMocks` does not inject string instances. How would it know which string to use? – knittl May 25 '23 at 05:45
  • I updated it to use `equals()` instead of `==` in `printSenderLetters()` but I get the same error – user5739619 May 25 '23 at 05:49
  • 1
    @user5739619 your new, updated code never uses `senderHeaders` (except for setting up the stubs). Where do you expect those method calls to happen? `Sender` is not using the headers, nor is `ATask`. – knittl May 25 '23 at 05:50
  • sorry that was a typo. Should be `senderPrint`. I updated the code – user5739619 May 25 '23 at 05:53
  • 1
    @user5739619 `sender` is a mock object. Mock objects don't have an implementation. `printSenderLetters` does nothing (has an empty implementation). The mock object doesn't even know that the real object depends on a `SenderPrinter`. So Mockito.verify is right in telling you that the methods never were invoked – they weren't. If you want to test integration of several components, use the real components and don't mock them (and then try to tell them to do the things the real components did) – knittl May 25 '23 at 05:55
  • It works if I set `sender`, `aTask`, `senderPrint` as spys. But how can I make it work while keeping them as mocks? – user5739619 May 25 '23 at 06:59
  • @user5739619 mocks, by definition, are _not_ your real classes. Mocks are bodyless and only do what you tell them. They don't have dependencies/collaborators and they do not call any other classes. Either you want to mock your collaborators or you want to use your real ones. If you are only interested in verification of method calls, `Mock.spy`/`@Spy` is the way to go. You can still stub/overwrite methods of a Mockito spy. – knittl May 25 '23 at 09:58
  • Additionally, your `captor` variable (in the last example) isn't used anywhere to capture anything. It is created, then its value is queried (which will be null). – knittl May 25 '23 at 11:12
  • 1
    If you're writing a unit test of `BSending` with a mock `Sender`, why are you mocking `SenderHeaders`? `BSending` doesn't use it. There is no reason to mock it in `BSendingTest`. – David Conrad May 25 '23 at 15:21

0 Answers0