square / retrofit

A type-safe HTTP client for Android and the JVM
https://square.github.io/retrofit/
Apache License 2.0
43.01k stars 7.3k forks source link

No way to add Header with MockRetrofit BehaviorDelegate (2.0.0-beta4) #1596

Closed ndorigatti closed 8 years ago

ndorigatti commented 8 years ago

Hello, I'm developing a routine to download about 40-50 pdfs and merge them together. I've mocked the calls to retrieve fakes pdfs locally in order to test UI and other stuff, but I have issues delaying the calls (basically the emulator does all the requests in a part of second and I can't check the notifications flow...).

My code is quite easy, here is how I get the retrofit objects with delay:

 private OkHttpClient getOkHttpClient() {
        if (null != okClient) return okClient;
        okClient = new OkHttpClient.Builder().addInterceptor(new AddCookiesInterceptor()).addInterceptor(new LoggingInterceptor()).build();
        return okClient;
    }

    private MyRetrofitService getRetrofitService() {
            if (null == mockRetrofitService) {
                NetworkBehavior behavior = NetworkBehavior.create();
                behavior.setDelay(5000, TimeUnit.MILLISECONDS);//
                behavior.setVariancePercent(0);//I've tried also with 80
                Retrofit retrofit = new Retrofit.Builder().client(getOkHttpClient())
                        .addConverterFactory(SimpleXmlConverterFactory.create())
                        .baseUrl(Util.ROOT_URL)
                        .build();
                MockRetrofit mockRetrofit = new MockRetrofit.Builder(retrofit).networkBehavior(behavior)
                        .build();
                BehaviorDelegate<MyRetrofitService> delegate = mockRetrofit.create(MyRetrofitService.class);
                mockRetrofitService = new MockMyRetrofit(delegate, getApplicationContext());
            }
            return mMockRetrofitService;
    }

I've changed some names to hide my real class names, but the problem is that calls are not delayed. From the code and from what i've understood on how NetworkBehavior works, each call should take 5000 milliseconds, and instead it takes just a part of second for ALL the 40-50 requests!

I'm using simple and classic ways to use retrofit, and sync version (execute).

Am I wrong on how to call the behavior or is it an issue of the beta?

Thanks!

ndorigatti commented 8 years ago

I made some tests in debug mode, it seems that going over "enqueue" does the delay, delaySleep() in BehaviorCall class is called correctly, while if doing "execute" nothing is hit (it is impossible to me, but I got no breakpoints hit).

To be more precise, AS gives me three option of execute() method implementations: BehaviorCall ExecutorCallbackCall OkHttpCall

I tryed setting a breakpoint on the first line of each function and none of them was hit.

Maybe I'm missing something but if so, I'm really far from understanding whoever gets that "execute" call...

ndorigatti commented 8 years ago

Hello, third comment to give more info...

I've tried more and found that it could be an issue on how I do the mock result, but I cant understand how to do differently. It seems that it is because I'm not using the "BehaviorDelegate" in the 'return' instruction of the Mock method, for example:

return delegate.returningResponse(buf.toString()).listPages();

Correctly hits the delay in BehaviorCall, while since I need to mock a service which gives back a PDF as response body and the "Content-Length" header, I'm doing:

            InputStream fileIS = ...; //Get file from assets as InputStream 
            long fileLength = 132456l;// calculated at runtime
            Buffer bf = new Buffer().readFrom(fileIS);

            okhttp3.Response rawResp = new okhttp3.Response.Builder() 
                    .code(200).message("OK")
                    .body(ResponseBody.create(MediaType.parse("application/pdf"), bf.size(), bf))  
                    .protocol(Protocol.HTTP_1_1)
                    .request(new Request.Builder().url("http://localhost").build())
                    .addHeader("Content-Length", Long.toString(fileLength)).
                            build();

            return Calls.response(Response.success(rawResp.body(), rawResp));

This way, not using the delegate means delay code in BehaviorCall is ignored, but I couldn't find a way to add a response header using the delegate... Is it possible?

EDIT: I forgot to say that: return delegate.returning(Calls.response(Response.success(rawResp.body(), rawResp)));

Obviosly does not work; java.lang.ClassCastException: $Proxy3 cannot be cast to retrofit2.Call at it.mypackage.retrofit.MockMyRetrofit.getPage(MockMyRetrofit.java:140)

Where the issue above is at the line of the "delegate.returning" call...

ndorigatti commented 8 years ago

Ok, I managed to make it work, I was not following correctly the classes (there is no documentation/examples but reading the code can be inferred):

the return line is now is: return delegate.returning(Calls.response(Response.success(rawResp.body(), rawResp))).getPage(DayPath, PagePath, pageNumber);

Where the signature of the method is: public Call<ResponseBody> getPage(@Path("DayPath") String DayPath, @Path("PagePath") String PagePath, @retrofit2.http.Header("page") int pageNumber)

In this way works. The issue can be closed. Sorry for bothering!

ndorigatti commented 8 years ago

A new and easier method has provided in #1619