简体   繁体   English

使用Mockito和jUnit为具有两个HTTP调用的方法编写单元测试

[英]Writing a unit test for a method with two HTTP calls with Mockito and jUnit

I have three methods, 我有三种方法,

  • void save(String a, String b) void save(String a,String b)
  • String getId(String a, Pojo p) String getId(String a,Pojo p)
  • void update(String a, String b, StringEntity c) void update(String a,String b,StringEntity c)

Here are the implementations(not the full implementation), 以下是实现(不是完整的实现),

String getId(String accessToken, Pojo p) {

    //Note that the pojo is for formatting the payload for the request, nothing more

    HttpResponse response = httpRequestService.makeGetRequest(url, TOKEN_PREFIX, accessToken,
            CONTENT_TYPE);
    if (response.getStatusLine().getStatusCode() == 200) {
        log.debug("Success");
    }

    //Code for getting the id from the response

    return id;
}

void update(String accassToken, String id, StringEntity payload) {

    HttpResponse response = httpRequestService.makePutRequest(url + id,
            TOKEN_PREFIX, accessToken, CONTENT_TYPE, payload);

    if (response.getStatusLine().getStatusCode() == 200) {
        log.debug("Success");
    }
}

void save(String accessToken, String payload) {

    //The getId() is called here
    String id = getId(/*arguments*/);

    if(id == null) {
        log.error("Error");
    } else {
        //The update() is called here
        update(/*arguments*/);
    }
}

As mentioned, the getId and update methods are called inside save method and both getId and update methods have HTTP calls. 如前所述,getId和update方法在save方法中调用,getId和update方法都有HTTP调用。

I have to write a unit test for the save() method. 我必须为save()方法编写单元测试。 This is what I tried. 这是我试过的。

    //This is an interface with methods to call HTTP requests.
    HttpRequestService httpRequestService = Mockito.mock(HttpRequestService.class);

    //Constructor injection
    ClassWithMethods a = new ClasswithMethods(httpRequestService);

    HttpResponse responseGet = Mockito.mock(HttpResponse.class);
    StatusLine statusLineGet = Mockito.mock(StatusLine.class);
    HttpEntity httpEntity = Mockito.mock(HttpEntity.class);
    Mockito.when(responseGet.getStatusLine()).thenReturn(statusLineGet);
    Mockito.when(statusLineGet.getStatusCode()).thenReturn(200);
    Mockito.when(responseGet.getEntity()).thenReturn(httpEntity);
    Mockito.when(httpEntity.getContent()).thenReturn(IOUtils.toInputStream(stream));
    Mockito.when(httpRequestService.makeGetRequest(url, TOKEN_PREFIX, accessToken,
            CONTENT_TYPE).thenReturn(responseGet);

    HttpResponse responsePut = Mockito.mock(HttpResponse.class);
    StatusLine statusLinePut = Mockito.mock(StatusLine.class);
    Mockito.when(responsePut.getStatusLine()).thenReturn(statusLinePut);
    Mockito.when(statusLinePut.getStatusCode()).thenReturn(200);
    Mockito.when(httpRequestService.makePutRequest(url + id, TOKEN_PREFIX,
            accessToken, CONTENT_TYPE, payloadEntity).thenReturn(responsePut);

    a.save(accessToken, payload);

But when testing, the responsePut returns a null. 但是在测试时,responsePut返回null。 The arguments are also matching. 参数也是匹配的。

Problem : How to test this save method which calls two HTTP methods? 问题 :如何测试这个调用两个HTTP方法的save方法?

This may not be the best way to do this. 这可能不是最好的方法。 If there better ways to test a method like this, please suggest. 如果有更好的方法来测试这样的方法,请建议。

Thank you 谢谢

Note that, httpRequestService is an interface with methods for HTTP calls and the constructor injection is used too. 请注意,httpRequestService是一个带有HTTP调用方法的接口,也使用构造函数注入。

You should extract the following into a separate method (same goes for PUT): 您应该将以下内容提取到一个单独的方法中(PUT也是如此):

HttpResponse response = httpRequestService.makeGetRequest(url, TOKEN_PREFIX, accessToken,
            CONTENT_TYPE);

Then you can stub that method using mockito's: 然后你可以使用mockito来存根该方法:

when(obj.methodGET(...)).thenReturns(...)

Remember : your unit-tests should not make calls over-the-wire! 请记住 :你的单元测试应该过度的电线来电!

EDIT 编辑

private HttpResponse get(String url, ...) {
    return httpRequestService.makeGetRequest(url, TOKEN_PREFIX, accessToken,
            CONTENT_TYPE);
}


private HttpResponse put(String url, ...) {
    return httpRequestService.makePutRequest(url + id,
        TOKEN_PREFIX, accessToken, CONTENT_TYPE, payload);
}

And then: 接着:

...
when(mockedStatusLineObj.getStatusCode()).thenReturns(200);
HttpResponse mockedGet = mock(HttpResponse.class);
when(mockedGet.getStatusLine()).thenReturns(mockedStatusLineObj);
when(object.get(...)).thenReturns(mockedGet);
when(object.put(...)).thenReturns(...);

About your way of testing 关于你的测试方式

Actually, your test setup is fine but complicated to read. 实际上,您的测试设置很好,但阅读起来很复杂。
A test has to keep readable and simple. 测试必须保持可读性和简单性。
Otherwise understanding and maintaining it becomes hard. 否则理解和维护它变得困难。 You want to mock HttpRequestService . 你想模拟HttpRequestService
It is a good way if you want to unit-test your class and don't want consider integration concerns. 如果您想对您的课程进行unit-test并且不想考虑集成问题,这是一种好方法。
But the way you mock dependencies of the mocked HttpRequestService makes the code not readable : 但是,模拟模拟HttpRequestService依赖关系的方式使得代码不可读:

HttpResponse responsePut = Mockito.mock(HttpResponse.class);
StatusLine statusLinePut = Mockito.mock(StatusLine.class);
Mockito.when(responsePut.getStatusLine()).thenReturn(statusLinePut);
Mockito.when(statusLinePut.getStatusCode()).thenReturn(200);

And you repeat this at each invocation. 并在每次调用时重复此操作。
Extract them in a method : 用一种方法提取它们:

public HttpResponse createMockedHttpResponse(String url){
    HttpResponse responsePut = Mockito.mock(HttpResponse.class);
    StatusLine statusLinePut = Mockito.mock(StatusLine.class);
    Mockito.when(responsePut.getStatusLine()).thenReturn(statusLinePut);
    Mockito.when(statusLinePut.getStatusCode()).thenReturn(200);
    return responsePut;
}

About the test failure 关于测试失败

It just throws a NullPointerException at the line, 它只是在行处抛出NullPointerException

if (response.getStatusLine().getStatusCode() == 200) in the update() method. update()方法中的if (response.getStatusLine().getStatusCode() == 200)

When I debugged it, it shows the responsePut is null. 当我调试它时,它显示responsePut为null。

If I look your actual code, these invocations in the class under test : 如果我查看您的实际代码,则在测试的类中进行以下调用:

getStatusLine().getStatusCode()

are correctly mocked here : 在这里被正确嘲笑:

HttpResponse responsePut = Mockito.mock(HttpResponse.class);
StatusLine statusLinePut = Mockito.mock(StatusLine.class);
Mockito.when(responsePut.getStatusLine()).thenReturn(statusLinePut);
Mockito.when(statusLinePut.getStatusCode()).thenReturn(200);

By elimination, it means that the recorded behavior at the next statement was not encountered: 通过消除,这意味着没有遇到下一个语句中记录的行为:

Mockito.when(httpRequestService.makePutRequest(url + id, TOKEN_PREFIX,
        accessToken, CONTENT_TYPE, payloadEntity).thenReturn(responsePut);

So ensure you that the record behavior for makePutRequest() and the actual invocation of makePutRequest() have exactly same / equals() passed parameters. 所以确保你备案行为makePutRequest()和实际调用makePutRequest()具有完全相同的/ equals()传递的参数。
If some parameters are not really required for the test logic, don't hesitate to pass Mockito.any() 如果测试逻辑并不真正需要某些参数,请不要犹豫,通过Mockito.any()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM