Mockito在模拟POST请求时出现InvalidUseOfMatchersException错误。

huangapple 未分类评论55阅读模式
英文:

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 = &quot;0&quot; ;
		ResteasyClient httpClient = new ResteasyClientBuilder().connectionPoolSize(1)
				.establishConnectionTimeout(10, TimeUnit.SECONDS).socketTimeout(10, TimeUnit.SECONDS).build();
		ResteasyWebTarget target = httpClient.target(path) ;

		Map&lt;String, Object&gt; request = new HashMap&lt;&gt;();
		request.put(&quot;operation&quot;, &quot;dislocation&quot;);
		request.put(&quot;direction&quot;, &quot;right&quot;);
		request.put(&quot;amount&quot;, &quot;2&quot;);
		request.put(&quot;unit&quot;, &quot;metric&quot;);
		String requestJson = new ObjectMapper().writeValueAsString(request);

		wsResponse = target.request().post(Entity.entity(requestJson, &quot;application/json&quot;));
		String responseString = wsResponse.readEntity(String.class);
		ObjectMapper responseMapper = new ObjectMapper();
		@SuppressWarnings(&quot;unchecked&quot;)
		Map&lt;String, Object&gt; response = (Map&lt;String, Object&gt;) responseMapper.readValue(responseString, Map.class);
		
		if (response != null) {
			newHome = (String) response.get(&quot;coordinate&quot;);
		}
		// 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&lt;String, Integer&gt; outcomeMap = new HashMap&lt;String, Integer&gt;() ;
		outcomeMap.put(&quot;coordinate&quot;, 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(&quot;url&quot;) ;
		
		// 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:

-&gt; 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(&quot;foo&quot;))

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

huangapple
  • 本文由 发表于 2020年5月30日 00:51:15
  • 转载请务必保留本文链接:https://java.coder-hub.com/62090900.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定