1

I have a db service save() method which allows method chaining:

@Service
public class Service {
...
    public Service save(...) {
        ...     
        return this;
    }

and this works just great as:

service.save(this).save(that).save(other);

When I come to mock it with Mockito though it breaks unless I use

Service serviceMock = mock(Service.class, RETURNS_DEEP_STUBS); 

IIUC though, the use of RETURNS_DEEP_STUBS is considered bad. Is there a better way to mock a class with method call chaining?

Ian
  • 1,507
  • 3
  • 21
  • 36
  • How about creating a method `void save(Object... entities)` that would be used like `service.save(thiz, that, other)`? – SpaceTrucker Sep 26 '16 at 06:33
  • That would certainly fix this example, but I'm really more interested in the general case. – Ian Sep 26 '16 at 17:34

1 Answers1

2

Your pattern for save is very similar to a Builder pattern, which makes the question similar to "How to mock a builder with mockito" elsewhere on SO.

Per David Wallace's answer there, you can write an Answer that detects whether the mock is an instance of the return type of the method, and return the mock in only that case. This functionality was also built into the Mockito library as RETURNS_SELF in Mockito 2.0. As with any Answer, you can use this on any specific method call with thenAnswer or as the second parameter of mock to make it the default answer, but bear in mind the Mockito documentation warning that methods with generous return types (e.g. Object) will return the mock whether or not that was intended.

Community
  • 1
  • 1
Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251
  • I'm not convinced that RETURNS_DEEP_STUBS /is/ bad in the case. This author, http://tuhrig.de/everytime-a-mock-returns-a-mock-a-fairy-dies/, seems to agree. But I've found another reason for not doing method chaining: Mockito only records ONE call to save() in the above example rather than three. – Ian Oct 10 '16 at 06:45
  • I wouldn't say that RETURNS_DEEP_STUBS doesn't have a place, but it seems like it is at least _unrealistic_ here: Your builder pattern likely guarantees that `instance.save(foo) -> instance` and RETURNS_DEEP_STUBS violates that. It may not break in all implementations, but if you're trying to make an implementation-agnostic test, you'll want to keep to that so you don't get brittle tests that break while the system-under-test works. (Also, rightly noted about needing to be careful about `save` and verification in general, while RETURNS_SELF is a little more intuitive.) – Jeff Bowman Oct 10 '16 at 17:35
  • Good points. I must admit I've been a bit reluctant to step up to M2.0 whilst it seems quiesced in development. – Ian Oct 10 '16 at 21:04