How do I mock an implementation class?

10,288

Solution 1

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);
  }
}

Solution 2

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!

Solution 3

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);
  }
}
Share:
10,288
user87407
Author by

user87407

Updated on June 27, 2022

Comments

  • user87407
    user87407 almost 2 years

    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
    user87407 about 7 years
    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?
  • Andy Turner
    Andy Turner about 7 years
    Dependency injection does not mean using a DI framework: it just means you pass dependencies into the instance, somehow - constructor parameters, setters, DI framework...
  • user87407
    user87407 about 7 years
    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.
  • Andreas
    Andreas about 7 years
    If you show me what you tried, I'll be glad to help.
  • GhostCat
    GhostCat about 7 years
    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.