英文:
Mockito InvalidUseOfMatchersException when mocking a post request
问题
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import java.util.HashMap;
import java.util.Map;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import org.jboss.resteasy.client.jaxrs.ResteasyWebTarget;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import externalTests.ClassA;
@RunWith(MockitoJUnitRunner.class)
public class DispositionMockTest {
@Mock
ResteasyWebTarget targetMock;
@InjectMocks
ClassA classA;
@Test
public void dispositionTest() throws Exception {
// Given
Map<String, Integer> outcomeMap = new HashMap<>();
outcomeMap.put("coordinate", 4);
Response resp = Response.status(Status.OK).type(MediaType.APPLICATION_JSON).entity(outcomeMap).build();
when(targetMock.request().post(any(Entity.class))).thenReturn(resp);
// When
int result = classA.moveActual("url");
// Then
assertEquals(4, result);
}
}
Please note that this code is a direct translation of your original code into Chinese. If you have any questions or need further assistance, feel free to ask.
英文:
I'm totally new to mockito and I have tried a lot of solutions I found online but I still can't solve this problem. I'm not even sure what is causing it. I need to create a mock test for a very mature code which I am not allowed to change. The part of the code which I need to mock sends a post request to a server. I have recreated the code in a much simpler way but unfortunately, it is still rather long. Lets say I have ClassA which calls the api as shown bellow and returns an integer.
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Response;
import org.jboss.resteasy.client.jaxrs.ResteasyClient;
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
import org.jboss.resteasy.client.jaxrs.ResteasyWebTarget;
import com.fasterxml.jackson.databind.ObjectMapper;
public class ClassA {
public int moveActual(String path) throws IOException {
Response wsResponse = null;
String newHome = "0" ;
ResteasyClient httpClient = new ResteasyClientBuilder().connectionPoolSize(1)
.establishConnectionTimeout(10, TimeUnit.SECONDS).socketTimeout(10, TimeUnit.SECONDS).build();
ResteasyWebTarget target = httpClient.target(path) ;
Map<String, Object> request = new HashMap<>();
request.put("operation", "dislocation");
request.put("direction", "right");
request.put("amount", "2");
request.put("unit", "metric");
String requestJson = new ObjectMapper().writeValueAsString(request);
wsResponse = target.request().post(Entity.entity(requestJson, "application/json"));
String responseString = wsResponse.readEntity(String.class);
ObjectMapper responseMapper = new ObjectMapper();
@SuppressWarnings("unchecked")
Map<String, Object> response = (Map<String, Object>) responseMapper.readValue(responseString, Map.class);
if (response != null) {
newHome = (String) response.get("coordinate");
}
// In the real code there is much more code here.
return Integer.parseInt(newHome) ;
}
}
I need to mock the server to test the moveActual method. So I decided to mock the post method. In order to do that, I came up with the test below:
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatchers;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import static org.mockito.BDDMockito.given;
import java.util.HashMap;
import java.util.Map;
import org.jboss.resteasy.client.jaxrs.ResteasyWebTarget;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import externalTests.ClassA;
@RunWith(MockitoJUnitRunner.class)
public class DispositionMockTest {
@Mock
ResteasyWebTarget targetMock;
@InjectMocks
ClassA classA;
@Test
public void dispositionTest() throws Exception {
// Given
Map<String, Integer> outcomeMap = new HashMap<String, Integer>() ;
outcomeMap.put("coordinate", 4) ;
Response resp = Response.status(Status.OK).type(MediaType.APPLICATION_JSON).entity(outcomeMap).build() ;
given(targetMock.request().post(ArgumentMatchers.isA(Entity.class))).willReturn(resp) ;
// When
int result = classA.moveActual("url") ;
// Then
assertEquals(4, result);
}
}
However, I get the error bellow no matter what I do.
java.lang.NullPointerException
at externalTests.DispositionMockTest.dispositionTest(DispositionMockTest.java:42)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.mockito.internal.runners.DefaultInternalRunner$1$1.evaluate(DefaultInternalRunner.java:46)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.mockito.internal.runners.DefaultInternalRunner$1.run(DefaultInternalRunner.java:77)
at org.mockito.internal.runners.DefaultInternalRunner.run(DefaultInternalRunner.java:83)
at org.mockito.internal.runners.StrictRunner.run(StrictRunner.java:39)
at org.mockito.junit.MockitoJUnitRunner.run(MockitoJUnitRunner.java:163)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:89)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:542)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:770)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:464)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:210)
org.mockito.exceptions.misusing.InvalidUseOfMatchersException:
Misplaced or misused argument matcher detected here:
-> at externalTests.DispositionMockTest.dispositionTest(DispositionMockTest.java:42)
You cannot use argument matchers outside of verification or stubbing.
Examples of correct usage of argument matchers:
when(mock.get(anyInt())).thenReturn(null);
doThrow(new RuntimeException()).when(mock).someVoidMethod(anyObject());
verify(mock).someMethod(contains("foo"))
This message may appear after an NullPointerException if the last matcher is returning an object
like any() but the stubbed method signature expect a primitive argument, in this case,
use primitive alternatives.
when(mock.get(any())); // bad use, will raise NPE
when(mock.get(anyInt())); // correct usage use
Also, this error might show up because you use argument matchers with methods that cannot be mocked.
Following methods *cannot* be stubbed/verified: final/private/equals()/hashCode().
Mocking methods declared on non-public parent classes is not supported.
at org.mockito.internal.runners.DefaultInternalRunner$1$2.testFinished(DefaultInternalRunner.java:68)
at org.junit.runner.notification.SynchronizedRunListener.testFinished(SynchronizedRunListener.java:56)
at org.junit.runner.notification.RunNotifier$7.notifyListener(RunNotifier.java:190)
at org.junit.runner.notification.RunNotifier$SafeNotifier.run(RunNotifier.java:72)
at org.junit.runner.notification.RunNotifier.fireTestFinished(RunNotifier.java:187)
at org.junit.internal.runners.model.EachTestNotifier.fireTestFinished(EachTestNotifier.java:38)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:331)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.mockito.internal.runners.DefaultInternalRunner$1.run(DefaultInternalRunner.java:77)
at org.mockito.internal.runners.DefaultInternalRunner.run(DefaultInternalRunner.java:83)
at org.mockito.internal.runners.StrictRunner.run(StrictRunner.java:39)
at org.mockito.junit.MockitoJUnitRunner.run(MockitoJUnitRunner.java:163)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:89)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:542)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:770)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:464)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:210)
I have tried @spy
instead of @mock
and other matchers like ArgumentMatchers.any(Entity.class)
and ArgumentMatchers.any()
but to no avail. I was looking into powermock but none of the methods and classes involved are private, nor static. I have no idea what else to try. I appreciate any help.
答案1
得分: 0
因为您对 ResteasyWebTarget
进行了模拟,并且没有定义 request()
方法的返回内容,所以您得到了那个异常。
您应该首先模拟请求并将其返回给 request()
方法,然后从 mockedRequest
定义到 post()
方法的行为。
when(targetMock.request()).thenReturn(mockedRequest);
when(mockedRequest.post(any(Entity.class))).thenReturn(resp);
英文:
Because you mocked ResteasyWebTarget
and didn't define what should be the return of request()
method, you got that exception.
You should first mock request and return it to the request()
method and define the behavior to post()
method from mockedRequest
when(targetMock.request()).thenReturn(mockedRequest);
when(mockedRequest.post(any(Entity.class))).thenReturn(resp) ;
专注分享java语言的经验与见解,让所有开发者获益!
评论