Java JUnit/Mockito验证`any(HttpPut.class)`但是当其他实例也被使用时会通过

Java JUnit/Mockito验证`any(HttpPut.class)`但是当其他实例也被使用时会通过,java,junit,mockito,Java,Junit,Mockito,我有一个服务类,它调用RESTAPI来获取、创建、更新和删除订阅者。Uri保持不变,但HTTP方法会如您所期望的那样更改。我想测试给出的正确方法。下面是updateSubscriber及其测试的示例 public class MyService { HttpClient httpClient; public MyService(HttpClient httpClient) { this.httpClient = httpClient; } /

我有一个服务类,它调用RESTAPI来获取、创建、更新和删除订阅者。Uri保持不变,但HTTP方法会如您所期望的那样更改。我想测试给出的正确方法。下面是updateSubscriber及其测试的示例

public class MyService {

    HttpClient httpClient;

    public MyService(HttpClient httpClient) {
        this.httpClient = httpClient;
    }

    //...

    public int updateSubscriber(Subscriber subscriber) throws ... {

        // PUT is the correct method for this request 
        HttpResponse response = httpClient.execute( new HttpPut( "https://example.org/api/subscribers" ) );

        //...
    }

    //...
下面是我对JUnit和Mockito的测试:

@RunWith(MockitoJUnitRunner.class)
public class MyServiceTest 
{

    @Mock
    private HttpClient mockHttpClient;

    @Mock 
    private HttpResponse mockResponse;

    @Mock 
    private StatusLine mockStatusline;

    @Mock 
    private HttpEntity mockEntity;

    // test subject
    private MyService myService;

    @Before
    public void setup() {

        // // this will just ensure http* objects are returning our mocked instances so we can manipulate them..
        // when(mockHttpClient.execute(any(HttpGet.class))).thenReturn(mockResponse);
        // when(mockHttpClient.execute(any(HttpPost.class))).thenReturn(mockResponse);
        // when(mockHttpClient.execute(any(HttpPut.class))).thenReturn(mockResponse);
        // when(mockHttpClient.execute(any(HttpDelete.class))).thenReturn(mockResponse);

        // when(mockResponse.getStatusLine()).thenReturn(mockStatusline);
        // when(mockStatusline.getStatusCode()).thenReturn(HttpStatus.SC_OK);

        myService = new MyService(mockHttpClient);
    }

    @Test
    public void testUpdateSubscriber() throws ...
    {   

        when(mockHttpClient.execute(any(HttpPut.class))).thenReturn(mockResponse);

        when(mockResponse.getStatusLine()).thenReturn(mockStatusline);
        when(mockStatusline.getStatusCode()).thenReturn(HttpStatus.SC_OK);

        String responseString = "...";

        // this is consumed by a static method which we cannot mock, so we must deal with an actual entity instance 
        BasicHttpEntity entity = new BasicHttpEntity();
        entity.setContent(new ByteArrayInputStream(responseString.getBytes()));
        when(mockResponse.getEntity()).thenReturn(entity);  

        // create a test case Subscriber instance 
        Subscriber subscriber = new Subscriber();

        int statusCode = myService.updateSubscriber(subscriber);

        assertEquals(HttpStatus.SC_OK, statusCode);

        // just confirm that an HTTP request was made 
        // TODO this isn't working, still passes when wrong Http* method used 
        verify(mockHttpClient, times(1)).execute(any(HttpPut.class));
    }

    //...
但是,当我(错误地)拥有另一个Http*方法实例时,它仍然传递:

// this is wrong, and should fail, but passed :(
HttpResponse response = httpClient.execute( new HttpGet( "https://example.org/api/subscribers" ) ); 

我真的希望能够测试这一点,因为如果方法错误,执行的操作可能是错误的。此测试旨在确保PUT方法正确用于updateSubscriber的HTTP请求。有什么想法吗?

测试通过,因为
HtppPut
HttpGet
都是
HttpRequestBase
的实现类,将模拟从
HttpRequestBase
类更改为
HttpPut

 when(mockHttpClient.execute(any(HttpPut.class))).thenReturn(mockResponse);

因此,现在如果您尝试使用
GET
调用测试将失败,因为
GET
调用没有存根不确定这是否是我问题的正确答案,但我使用自定义参数匹配器成功地使测试按预期工作:

package uk.ac.strath.matchers;

import org.apache.http.client.methods.HttpUriRequest;
import org.mockito.ArgumentMatcher;

public class HttpMethodMatcher implements ArgumentMatcher<HttpUriRequest> {

    private String expectedClassName;

    // constructors
    public HttpMethodMatcher(String expectedClassName) {
        this.expectedClassName = expectedClassName;
    }

    @Override
    public boolean matches(HttpUriRequest httpMessage) {
        if (httpMessage.getClass().getName().equals(expectedClassName)) {
            return true;
        }

        return false;
    }
}

本教程很有帮助:

我想如果我通过了
HttpRequestBase.class
in,并使用
验证
,会是这样吗?我只是告诉
when
在使用HttpRequestBase的任何实现时返回相同的模拟实例。不管怎样,我已经按照你的建议进行了更改,并更新了上面的代码,但是,即使我正在验证另一个类实例是否已传递给
execute
方法,也只传递相同的结果。我不知道为什么要在
setup
方法中添加存根,以及为什么需要所有类型的存根@Martyn请查看一些spring junit示例在每次测试之前都不运行安装程序?我把它们放在这里是因为我不想在每个测试方法中重复相同的代码。我不认为在每个方法中都会调用相同的
put
request@MartynOK,为了排除这种情况,我已经将这些行从设置中移到了测试方法中。我在运行测试时也遇到了同样的问题。我更新了我原来的帖子。你对mockito的看法是什么?在1.x中,任何(类)的行为都是不同的。您是否调用此
httpClient.execute(新的HttpGet(“https://example.org/api/subscribers" ) );直接在
httpClient
mockHttpClient
上?上面的代码显示您没有对mock进行
GET
调用,并且还添加了一条语句来验证
GET
调用
verify(mockHttpClient,times(1)).execute(any(HttpGet.class))在测试中,您将看到我作为依赖项传入mockHttpClient,因此这个实例接收HttpGet(在其他情况下是Http*whetever方法)。我使用的是Mockito版本1.9.5。
verify(mockHttpClient, times(1)).execute( argThat(new HttpMethodMatcher( HttpGet.class.getName() )) );