Home > Back-end >  How to mock an intermediate class in the code flow in Java
How to mock an intermediate class in the code flow in Java

Time:05-16

I have 3 classes as shown below and I want to write a Unit test for validate() of Class A. I do not want to mock Class B. I want to mock Class C and always return same string when getDetails() is called. Is it possible with Mockito? If not, is there any alternative?

Class A{
  
  void validate(){
    B b = new B();
    b.verify();
  }
}

Class B throws Exception{
  void verify(){
    
    C c = new C();
    c.getDetails();
  }
}

Class C{
  String getDetails(){
    //Does an API call and returns the part of response as a string
  }
}

CodePudding user response:

You can use PowerMock for this. See the following example:

@RunWith(PowerMockRunner.class)
@PrepareForTest(C.class)
public class Test {

  @Test
  public void testMockingC() throws Exception {
    PowerMockito.stub(PowerMockito.method(C.class, "getDetails")).toReturn("Mocked successfully!");
    A a = new A();
    a.validate();
  }
}

Since no method returns the relevant value I couldn't do some assertion, but you can put a breakpont and track the program flow while running this test, c.getDetails() will return Mocked successfully!.

CodePudding user response:

B doesn't seem to let you pass in a C, so it's hard to substitute the C used with a test double. The problem is that B knows all the details of C's construction even when it doesn't (and shouldn't) care. Dependency injection is an important pattern: something like

interface DetailsProvider {
    public String getDetails();
}

class B {
   public B(DetailsProvider detailsProvider) {
       this.detailsProvider = detailsProvider;
   }

   private DetailsProvider detailsProvider;
}

and then

class FakeDetailsProvider implements DetailsProvider {
    @Override
    String getDetails() { 
        return "foo"; 
    }
}

so that in the test you can write

B someB = new B(new FakeDetailsProvider());

and in the production code, of course

class C implements DetailsProvider {
    ...
}

I've introduced the interface as abstraction - so that clients using it only know what operations (i.e. getDetails) they can perform, without being tied to any concrete implementation.

Note that this may not compile; I've not written Java for a while now!

  • Related