Mockito verify method call inside void method

Viewed 1466

I have the following class and method get which I want to test:

public class MyClass{
    private MyObject svc;
    private MyObject2 svc2;

    public MyClass(MyObject svc){
        this.svc = svc;
        this.svc2 = new MyObject2(svc);
    }


    public void getSvc(){
        Result response = this.svc.getResponse();
        if(!response.isEmpty())
             this.svc2.filterResponse(response);
        else
            System.out.println("empty");
    }

}

But I am struggling with being void method. How can I test that filterResponse will only get called when my response is not empty?

3 Answers

This is a design issue as the subject under test is not being honest about what it truly needs to perform its designed function. It is tightly coupling itself via new MyObject2(...) in a way that makes it difficult (not impossible) to test in isolation.

That fact that MyObject2 depends on MyObject is an implementation detail that MyClass need not be aware of.

Following explicit dependency principle,

Methods and classes should explicitly require (typically through method parameters or constructor parameters) any collaborating objects they need in order to function correctly.

MyClass should be refactored accordingly

public class MyClass{
    private MyObject svc;
    private MyObject2 svc2;

    public MyClass(MyObject svc, MyObject2 svc2){
        this.svc = svc;
        this.svc2 = svc2;
        //this.svc2 = new MyObject2(svc); // <-- remove implementation details
    }

    public void getSvc(){
        Result response = this.svc.getResponse();
        if(!response.isEmpty())
             this.svc2.filterResponse(response);
        else
            System.out.println("empty");
    }
}

Allowing for the necessary mocks/stubs to be explicitly injected into the subject under test when testing in isolation.

//Arrange
MyObject svc = mock(MyObject.class);
MyObject2 svc2 = mock(MyObject2.class);

MyClass subject = new MyClass(svc, svc2);

Result response = mock(Result.class);
when(response.isEmpty()).thenReturn(false);

when(svc.getResponse()).thenReturn(response);

//Act
subject.getSvc();

//Assert
verify(svc2, times(1)).filterResponse(response); //verify method call inside void method

And the fact that the method under test is void is of no consequence of the behavior being verified.

If this design is intended to be, and you cannot modify existing code. Here is an approach that you can do:

  1. Inside your Test class. Declare new static inner class (eg: InternalMyClass), which extends your MyClass
  2. Add new constructor to your inner class, with 2 parameters.
  3. Now inside your test case, construct your MyClass with your inner class. You can pass your spy/stub for MyObject2 now.

You Should autowire or injectMock your myClass as it should create a bean ,so that when you call the getSvc() it should satisfy your condition

Related