1

I'm writing some Java unit tests for an existing class using Mockito. However, I'm having trouble extracting the arguments with which an object of this class is calling a method as part of a nested call.

Example:

public class SomeClass {

  public SomeClass() {
    //...
  }

  public someMethod(Foo foo) {
    // Bar bar = ...
    someOtherMethod(bar);
  }

  private someOtherMethod(Bar bar) {
    //...
  }
}

I want to capture the arguments to someOtherMethod() and verify that bar is what it should be. Can I do this using ArgumentCaptor or other functionality within Mockito?

lstyls
  • 173
  • 2
  • 13

3 Answers3

2

Mocking someOtherMethod is unlikely to provide you a useful result. someMethod is part of your public API, but someOtherMethod is not; in general, it's important to test the contract (someMethod's return value and side effects) but it's an antipattern to test the implementation (the private methods that someMethod calls).

  • Ideally, just test the results and side-effects of someMethod, and consider its implementation a black box. You probably won't even need to use Mockito for this. This is the purest form of test-driven development: it frees you to refactor someMethod, maybe entirely omitting calls to someOtherMethod or caching the result (etc) without editing your test.
  • If someOtherMethod calls an external service, make it an accessible or settable field on your SomeClass instance and mock that service instead. Interactions with other services could reasonably be considered part of a general contract; this is really what Mockito was designed for.
  • If someOtherMethod is independent enough that you want to test someMethod separately, consider making it package-private or protected. That way you can override it with a dummy implementation in your test, and possibly elsewhere in your code—after all, it's now a tested part of your someMethod contract, right? You can also consider making it a separate mockable class, if it's that big or opaque.

See also "How do I unit test private methods?" in Programmers.SE, or "How to use Mockito when we cannot pass a mock object to an instance of a class" on StackOverflow.

Community
  • 1
  • 1
Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251
0

One option is to override SomeClass with a class that stores those arguments and makes them available to you for verification. An elegant option is custom argument matchers.

0

Yes.

@Spy
private SomeClass someClassSpy;

@Captor
private ArgumentCaptor<Bar> barCaptor;

...

verify(someClassSpy).someOtherMethod(barCaptor.capture());
Bar bar = barCaptor.getValue();
assertThat(bar, is(...));
akcasoy
  • 6,497
  • 13
  • 56
  • 100