-1

I have a java class and methods that I want to test with Mockito. These classes in their constructors or in other methods will construct objects and then use those objects in other methods.

I want to mock the behavior of those objects being used. The important thing is that none of these objects are being passed into the class. Rather, they are generated within the class itself. For example lets pretend this is a class I have:

private Client someClient;

public MyClass() {
    this.someClient = setupClient();
}

private setupClient() {...}

public methodIWantToTest() {
    this.someClient.makeApiCall(String requestString);
    // do stuff
}

In my test class and tests I want to be able to do something like:

// test class
...
MyClass classImGoingToTest; 

@Test
public void testMethodIWantToTest() {
    Mockito.when(Client.class).makeApiCall(Mockito.anyString()).thenDoSomething();
    ...
}

Of course Mockito needs to make frustratingly difficult to do basic things. So how can this be done in Mockito?

Other answers I read are either of the form "use powermockito" which i want to avoid right now, or "you shouldn't be testing this" which isn't an answer to my question, or they say "well you should pass in the values", which as I explained above is not possible.

Jeremy Fisher
  • 2,510
  • 7
  • 30
  • 59
  • I am afraid your research is correct. Mockito can't mock private field or method. without refactoring or using powermockito you could try to use old fashioned java java reflection to setup a test case. – Vlad Ulshin May 04 '23 at 02:32
  • 3
    Why "is it not possible" to pass in the values? The code as presented is not testable and its design needs to be changed, to account for testability. https://stackoverflow.com/q/74027324/112968 more or less describes the same problem and suggests several possible solutions. – knittl May 04 '23 at 06:12

2 Answers2

1

You'll need to make MyClass a wrapper class, and not test it, which is OK because it will be trivial:

class MyClass {
  private final MyTestableClass myTestableClass;

  public MyClass() {
    myTestableClass = new MyTestableClass(setupClient());
  }

  public void methodIWantToTest() {
    myTestableClass.methodIWantToTest();
  }
}

Then you can write a unit test for MyTestableClass, passing a mock Client to it's constructor.

(of course setupClient() is not trivial, but that wasn't being tested in your original test anyway.)

tgdavies
  • 10,307
  • 4
  • 35
  • 40
0

Configuring your dependency within the dependent class can be problematic. Depending on the complexity of your system you may want to explore dependency injection. Ignoring that..

Provide an alternate constructor that includes the dependency(ies) as a parameter.

public class MyClass {
  private Client someClient;

  public MyClass() {
    this(setupClient());
  }
  protected MyClass(Client client) {
    this.someClient = client;
  }

  private Client setupClient() {...}

  public methodIWantToTest() {
    this.someClient.makeApiCall(String requestString);
    // do stuff
  }
  ...
}

Define the mock in your test and pass it as an argument to the constructor.

public class MyClassTest {
  private MyClass SUT;
  // Dependencies
  private Client client = Mockito.mock(Client.class);

  @Before
  public void beforeTest() {
    SUT = new MyClass(client);
  }

  @Test
  public void testMethodIWantToTest() {
    Mockito.when(client.makeApiCall(Mockito.anyString())).thenDoSomething();
    .. test stuff
  }
}
vsfDawg
  • 1,425
  • 1
  • 9
  • 12