6

I have something like this:

public interface SomeInterface {
    public String someMethod(String someArg1, String someArg2);
}

public class SomeInterfaceImpl {

    @Override
    public String someMethod(String someArg1, String someArg2) {
        String response;

        // a REST API call which fetches response (need to mock this)

        return response;
    }
}

public class SomeClass {

    public int execute() {

        int returnValue;

        // some code

        SomeInterface someInterface = new SomeInterfaceImpl();
        String response = someInterface.someMethod("some1", "some2");

        // some code

        return returnValue;
    }
}

I want to test the execute() method in SomeClass using JUnit. Since someMethod(String someArg1, String someArg2) calls a REST API, I want to mock someMethod to return some predefined response. But somehow, the real someMethod gets called instead of returning the predefined response. How do I make it work?

Here is what I have tried using Mockito and PowerMockito:

@RunWith(PowerMockRunner.class)
@PrepareForTest({ SomeInterface.class, SomeInterfaceImpl.class, SomeClass.class })
public class SomeClassTest {

    @Test
    public void testExecute() {
        String predefinedResponse = "Some predefined response";
        int expectedReturnValue = 10;

        SomeInterfaceImpl impl = PowerMockito.mock(SomeInterfaceImpl.class);
        PowerMockito.whenNew(SomeInterfaceImpl.class).withAnyArguments().thenReturn(impl);
        PowerMockito.when(impl.someMethod(Mockito.any(), Mockito.any())).thenReturn(predefinedResponse);

        SomeClass someClass = new SomeClass();
        int actualReturnValue = someClass.execute();
        assertEquals(expectedReturnValue, actualReturnValue);
      }
}
user87407
  • 647
  • 3
  • 7
  • 24
  • Related? http://stackoverflow.com/questions/25317804/using-powermockito-whennew-is-not-getting-mocked-and-original-method-is-called – Andy Turner Feb 17 '17 at 20:39
  • 4
    This is *exactly* where dependency injection comes in. Don't use `new`, pass `SomeInterface` as a parameter. – chrylis -cautiouslyoptimistic- Feb 17 '17 at 20:39
  • @AndyTurner I tried `SomeInterface interface = PowerMockito.mock(SomeInterface.class)`, followed by `PowerMockito.when(interface.someMethod(Mockito.any(), Mockito.any())).thenReturn(predefinedResponse);`. It resulted into same problem. – user87407 Feb 17 '17 at 20:42
  • @AndyTurner: Had seen the related question. If you see my test, I have already taken care of that. – user87407 Feb 17 '17 at 20:44

3 Answers3

4

Here is an example of injecting dependencies without a framework:

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.when;

interface SomeInterface {
  String someMethod(String someArg1, String someArg2);
}

class SomeInterfaceImpl implements SomeInterface {

  @Override
  public String someMethod(String someArg1, String someArg2) {
    String response;

    response = "the answer.";// a REST API call which fetches response (need to mock this)

    return response;
  }
}

class SomeClass {
  private final SomeInterface someInterface;

  SomeClass(final SomeInterface someInterface) {
    this.someInterface = someInterface;
  }

  public SomeClass() {
    this(new SomeInterfaceImpl());
  }

  public int execute() {

    int returnValue;

    // some code

    String response = someInterface.someMethod("some1", "some2");

    returnValue = 42; // some code

    return returnValue;
  }
}

@RunWith(MockitoJUnitRunner.class)
class SomeClassTest {
  private static final String SOME_PREDEFINED_RESPONSE = "Some predefined response";
  @Mock
  private SomeInterface someInterface;
  @InjectMocks
  private SomeClass underTest;

  @Before
  public void setup() {
    when(someInterface.someMethod(anyString(), anyString())).thenReturn(SOME_PREDEFINED_RESPONSE);
  }

  @Test
  public void testExecute() {
    int expectedReturnValue = 42;
    int actualReturnValue = underTest.execute();
    assertEquals(expectedReturnValue, actualReturnValue);
  }
}
Andreas
  • 4,937
  • 2
  • 25
  • 35
1

You don't have to do that.

You change your method under test to NOT call new directly.

Instead you use dependency injection for example.

Yes, this could be done with Powermock, but please believe me: doing so is the wrong approach!

GhostCat
  • 137,827
  • 25
  • 176
  • 248
  • Does that mean, I need to add Spring support for dependency injection, or probably add a setter for `SomeInterfaceImpl`? I am afraid I am not allowed to refactor the code. Could you show how it can be done with PowerMock? – user87407 Feb 17 '17 at 20:46
  • 2
    Dependency injection does not mean using a DI framework: it just means you pass dependencies into the instance, somehow - constructor parameters, setters, DI framework... – Andy Turner Feb 17 '17 at 20:47
  • Also, `SomeInterface` has multiple implementations, being used in different classes like `SomeClass`. Which class to use, is decided at runtime based on some criterion, instantiated using reflection and its execute method is called. So it will not be possible to set the implementation of `SomeInterface` or pass it in the constructor. – user87407 Feb 17 '17 at 21:04
  • 1
    In that case your method should again not call new itself. It should be using some factory object, and factory should be coming in via dependency injection. – GhostCat Feb 18 '17 at 05:10
1

This answer is very similar to answer posted by Andreas, the only difference being you can run this with @RunWith(SpringRunner.class) and it will avoid problems with bean instantiation and extra configuration. Avoid Powermocks and use it only when you need to mock static classes.

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;

import org.springframework.test.context.junit4.SpringRunner;

import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.when;

interface SomeInterface {
  String someMethod(String someArg1, String someArg2);
}

class SomeInterfaceImpl implements SomeInterface {

  @Override
  public String someMethod(String someArg1, String someArg2) {
    String response;

    response = "the answer.";// a REST API call which fetches response (need to mock this)

    return response;
  }
}

class SomeClass {
  private final SomeInterface someInterface;

  SomeClass(final SomeInterface someInterface) {
    this.someInterface = someInterface;
  }

  public SomeClass() {
    this(new SomeInterfaceImpl());
  }

  public int execute() {

    int returnValue;

    // some code

    String response = someInterface.someMethod("some1", "some2");

    returnValue = 42; // some code

    return returnValue;
  }
}

@RunWith(MockitoJUnitRunner.class)
class SomeClassTest {
  private static final String SOME_PREDEFINED_RESPONSE = "Some predefined response";
  @Mock
  private SomeInterface someInterface;
  @InjectMocks
  private SomeClass underTest;

  @Before
  public void setup() {
    when(someInterface.someMethod(anyString(), anyString())).thenReturn(SOME_PREDEFINED_RESPONSE);
  }

  @Test
  public void testExecute() {
    int expectedReturnValue = 42;
    int actualReturnValue = underTest.execute();
    assertEquals(expectedReturnValue, actualReturnValue);
  }
}