How do I mock an implementation class?
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);
}
}
user87407
Updated on June 27, 2022Comments
-
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 inSomeClass
using JUnit. SincesomeMethod(String someArg1, String someArg2)
calls a REST API, I want to mocksomeMethod
to return some predefined response. But somehow, the realsomeMethod
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 about 7 yearsDoes 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 about 7 yearsDependency injection does not mean using a DI framework: it just means you pass dependencies into the instance, somehow - constructor parameters, setters, DI framework...
-
user87407 about 7 yearsAlso,
SomeInterface
has multiple implementations, being used in different classes likeSomeClass
. 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 ofSomeInterface
or pass it in the constructor. -
Andreas about 7 yearsIf you show me what you tried, I'll be glad to help.
-
GhostCat about 7 yearsIn 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.