0

I have 3 classes, as shown below, and I want to write a Unit test for the validate() method of class A. I do not want to mock class B. I want to mock class C and always return the same String when getDetails() is called. Is this 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
  }
}
Dan
  • 3,647
  • 5
  • 20
  • 26
Learner
  • 1
  • 1

2 Answers2

0

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!.

idanz
  • 821
  • 1
  • 6
  • 17
  • I was looking for a solution something similar to this. Unfortunately, this did not work when I tried running the test after adding the Powermockito stub. That resulted in calling the actual method getDetails() in which an API call is made. – Learner May 15 '22 at 22:03
  • Did you add the annotations? I debugged it and saw it's working before posting it. – idanz May 16 '22 at 05:47
  • Yes, I did add annotations. Tried with PowerMockito.suppress() as well. No luck. I must be missing something. – Learner May 16 '22 at 08:50
0

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.

The idea is to first introduce an abstraction, of which you can have different implementations:

interface DetailsProvider {
    public String getDetails();
}

There will be one implementation that makes the real API call, used in the production code and another that's completely in your control, for the tests.

Then, you make B take a DetailsProvider as a constructor parameter. The beauty in this is that it now no longer knows how the instance is created, making it easy to supply different versions of it. Anything implementing the interface will do.

class B {
   public B(DetailsProvider detailsProvider) {
       this.detailsProvider = detailsProvider;
   }
    public void verify () {
        String details = detailsProvider.getDetails();
        ...
    }

   private DetailsProvider detailsProvider;
}

For testing, we can have a fake:

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

so somewhere in your test setup, you have, e.g.

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

In the production code, C now needs to implement the interface:

class C implements DetailsProvider {
    @Override
    public String getDetails() {
        // Make the real API call.
    }
}

and again, you just wire the two up:

B myB = new B(new C());

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

ndc85430
  • 1,395
  • 3
  • 11
  • 17