12

I have a class I want to test that has several external dependencies, and a couple internal methods. I would like to write a test for MethodA, but not have Method A's internal call to MethodB to actually exercise MethodB. I'd like to mock/stub MethodB and return something specific instead. Usually I'd use when/thenReturn but it doesn't behave like I expect - it actually jumps into Method B while creating the mock itself.

MyService.java

@Service
public class MyService {

  @Autowired
  private ServiceA serviceA;

  @Autowired
  private ServiceB serviceB;

    public SomeObject methodA() {
      // some logic using serviceA.method and serviceB.method that creates "output"
      SomeObject someObject = methodB(output);
      return someObject;
    }

    public SomeObject methodB(SomeObject someObject) {
      // deep mysteries done here to someObject
      return someObject 
    }
}

MyServiceTest.java

public class MyServiceTest {

  @Mock
  private ServiceA serviceA;

  @Mock
  private ServiceB serviceB;

  @InjectMocks
  private MyService myService;

  @Before
  public void setUp() throws Exception {
    MockitoAnnotations.initMocks(this);
  }

  @Test
  public void methodATest() {
    when(serviceA.method()).thenReturn(stuff);
    when(serviceB.method()).thenReturn(otherStuff);

    // here is what I would like to do
    when(myService.methodB()).thenReturn(mockedSomeObject); //<- doesn't work

    assertThat(myService.methodA().getSomeObjectProperty())
        .isEqualTo("property");
  }
}

I've looked at solutions that manually mock the MyService class with Mockito.mock(MyService.class), but (as the above example is obviously contrived) my actual class has quite a few external dependencies and I'd prefer a solution that still allows me to mock the service using @Mock for the @Autowired dependencies and @InitMocks for the class under test, unless it's simply not possible.

I've tried:

Mockito.doReturn(mockedSomeObject).when(myService.methodB(any(SomeObject.class));

but that also steps into MethodB when creating the mock for that method, which shouldn't be happening.

Thanks in advance for the help!

Mikhail Kholodkov
  • 23,642
  • 17
  • 61
  • 78
tengen
  • 2,125
  • 3
  • 31
  • 57

2 Answers2

35

Try Adding @Spy to your InjectMocks and use the object to "expect" them in a slightly different syntax.

import org.mockito.Spy;

 @InjectMocks
 @Spy
 private MyService myService; 

And now mock the service call

 Mockito.doReturn(mockedSomeObject).when(myService).methodB();

Also change the other mock call to this

Mockito.doReturn(stuff).when(serviceA).method();
Mockito.doReturn(otherStuff).when(serviceB).method();
lrathod
  • 1,094
  • 1
  • 9
  • 17
  • Thank you - I had tried each of these changes on their own, but never together - this worked perfectly! – tengen Jun 05 '18 at 14:00
  • Happy that i was able to help you. – lrathod Jun 05 '18 at 14:24
  • 1
    @lrathod , there is any difference between using `when(serviceA.method()).ThenReturn(stuff)` and `doReturn(stuff).when(serviceA).method()` ?? – Eric Jun 18 '19 at 16:43
  • @Eric, When you are using the "Spy" , you will need to use the latter. Functionally both does the same though. – lrathod Jun 21 '19 at 09:22
  • AFAIK you wouldn't need to replace the mock calls to the other mocked `serviceA` and `serviceB`. Then remain as mocks (not spies), so there is no difference between `thenReturn` and `doReturn` – zr0gravity7 Dec 23 '22 at 23:47
1

You need to mark your object as Spy or explicitly create a Spy object for it using MyClass objA=null; MyClass spy_objA=Powermockito.spy(objA) doReturn(what_you_want).when(spy_objA).method()

Edit: Can find a similar question you may want to check How to mock another method in the same class which is being tested?

varkashy
  • 374
  • 4
  • 18
  • 1
    Thanks for the idea - however I'd prefer not to use PowerMockito unless there is really no way to accomplish this with "regular" Mockito. – tengen Jun 04 '18 at 16:50