欧美一区二区三区老妇人-欧美做爰猛烈大尺度电-99久久夜色精品国产亚洲a-亚洲福利视频一区二区

Roboletric+Retrofit2單元測試

目前的Android單元測試,很多都基于Roboletric框架,回避了Instrumentation test必須啟動虛擬機或者真機的麻煩,執(zhí)行效率大大提高。這里不討論測試框架的選擇問題,網(wǎng)絡上有很多關于此類的資料。同時,現(xiàn)在幾乎所有的App都會進行網(wǎng)絡數(shù)據(jù)通信,Retrofit2就是其中非常方便的一個網(wǎng)絡框架,遵循Restful接口設計。如此,再進行Android單元測試時,就必然需要繞過Retrofit的真實網(wǎng)絡請求,mock出不同的response來進行本地邏輯測試。

成都網(wǎng)站建設、網(wǎng)站建設的開發(fā),更需要了解用戶,從用戶角度來建設網(wǎng)站,獲得較好的用戶體驗。創(chuàng)新互聯(lián)公司多年互聯(lián)網(wǎng)經(jīng)驗,見的多,溝通容易、能幫助客戶提出的運營建議。作為成都一家網(wǎng)絡公司,打造的就是網(wǎng)站建設產(chǎn)品直銷的概念。選擇創(chuàng)新互聯(lián)公司,不只是建站,我們把建站作為產(chǎn)品,不斷的更新、完善,讓每位來訪用戶感受到浩方產(chǎn)品的價值服務。

retrofit官方出過單元測試的方法和介紹,詳見參考文獻4,介紹的非常細致。但是該方法是基于Instrumentation的,如果基于Robolectric框架,對于異步的請求就會出現(xiàn)問題,在stackoverflow上面有關于異步問題的描述,也給出了一個解決方法,但是需要對源碼進行改動,所以不完美。本文將針對Robolectric+Retrofit2的單元測試過程中異步問題如何解決,提出一種更完美的解決方法。有理解不當?shù)?,后者更好的方案,歡迎大家提出指正。

一般使用retrofit2的時候,會出現(xiàn)一下代碼片段

public void testMethod() {
    OkHttpClient client = new OkHttpClient();
    Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addConverterFactory(JacksonConverterFactory.create())
                .client(client)
                .build();
    service = retrofit.create(xxxService.class);
    Call<xxxService> call = service.getxxx();
    call.enqueue(new Callback<xxx>() {

        @Override
        public void onResponse(Call<xxx> call, Response<xxxResponse> response) {
        // Deal with the successful case
        }

        @Override
        public void onFailure(Call<xxxResponse> call, Throwable t) {
        // Deal with the failure case
        }
    });
}

單元測試會測試testMethod方法,觸發(fā)后根據(jù)不同的response,校驗對應的邏輯處理,如上面的“// Deal with the successful case” 和 “// Deal with the failure case”。為了達到這個目的,需要實現(xiàn)一下兩點:1)當觸發(fā)該方法時,不會走真實的網(wǎng)絡;2)可以mock不同的response進行測試

第一點可以借助MockWebServer來實現(xiàn),具體的實現(xiàn)方法可以參考文獻4,這里不展開了,重點看下第二點。在文獻4中的sample#1,通過一個json文件,清晰簡單的表明了測試的目的,所以我們也希望用這種方式。但是當實現(xiàn)后測試卻發(fā)現(xiàn),上面賦值給call.enqueue的Callback,無論是onResponse還是onFailure都不會被調(diào)用。后來在stackoverflow上面發(fā)現(xiàn)了文獻3,再結合自己的測試,發(fā)現(xiàn)根本的原因在于call.enqueue是異步的。當單元測試已經(jīng)結束時,enqueue的異步處理還沒有結束,所以Callback根本沒有被調(diào)用。那么網(wǎng)絡是否執(zhí)行了呢?通過打開OkhttpClient的log可以看到,MockWebServer的request和response都出現(xiàn)了,說明網(wǎng)絡請求已經(jīng)模擬執(zhí)行了。產(chǎn)生這個問題跟Robolectric框架的實現(xiàn)有一定的關系,更進一步的具體原因,有興趣大家可以進一步研究,也許會發(fā)現(xiàn)新的思路。

知道是由于異步導致的,那解決的思路就簡單了,通過mock手段,將異步執(zhí)行變成同步執(zhí)行。那么如何mock呢,我們可以通過retrofit的源碼來查看。

通過Retrofit的create方法可以獲取service,先來看看create這個方法的實現(xiàn)

public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
        eagerlyValidateMethods(service);
    }
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
            new InvocationHandler() {
                private final Platform platform = Platform.get();

                @Override public Object invoke(Object proxy, Method method, Object... args)
                        throws Throwable {
                    // If the method is a method from Object then defer to normal invocation.
                    if (method.getDeclaringClass() == Object.class) {
                        return method.invoke(this, args);
                    }
                    if (platform.isDefaultMethod(method)) {
                        return platform.invokeDefaultMethod(method, service, proxy, args);
                    }
                    ServiceMethod serviceMethod = loadServiceMethod(method);
                    OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
                    return serviceMethod.callAdapter.adapt(okHttpCall);
                }
            });
}

從代碼可以看出,通過service.getxxx()來獲得Call<xxxService>的時候,實際獲得的是OkHttpCall。那么call.enqueue實際調(diào)用的也是OkHttpCall的enqueue方法,其源碼如下:

@Override public void enqueue(final Callback<T> callback) {
	if (callback == null) throw new NullPointerException("callback == null");

	okhttp3.Call call;
	Throwable failure;

	synchronized (this) {
		if (executed) throw new IllegalStateException("Already executed.");
		executed = true;

		call = rawCall;
		failure = creationFailure;
		if (call == null && failure == null) {
			try {
				call = rawCall = createRawCall();
			} catch (Throwable t) {
				failure = creationFailure = t;
			}
		}
	}

	if (failure != null) {
		callback.onFailure(this, failure);
		return;
	}

	if (canceled) {
		call.cancel();
	}

	call.enqueue(new okhttp3.Callback() {
		@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
				throws IOException {
			Response<T> response;
			try {
				response = parseResponse(rawResponse);
			} catch (Throwable e) {
				callFailure(e);
				return;
			}
			callSuccess(response);
		}

		@Override public void onFailure(okhttp3.Call call, IOException e) {
			try {
				callback.onFailure(OkHttpCall.this, e);
			} catch (Throwable t) {
				t.printStackTrace();
			}
		}

		private void callFailure(Throwable e) {
			try {
				callback.onFailure(OkHttpCall.this, e);
			} catch (Throwable t) {
				t.printStackTrace();
			}
		}

		private void callSuccess(Response<T> response) {
			try {
				callback.onResponse(OkHttpCall.this, response);
			} catch (Throwable t) {
				t.printStackTrace();
			}
		}
	});
}

這里通過createRawCall方法來獲得真正執(zhí)行equeue的類,再看看這個方法的實現(xiàn):

private okhttp3.Call createRawCall() throws IOException {
	Request request = serviceMethod.toRequest(args);
	okhttp3.Call call = serviceMethod.callFactory.newCall(request);
	if (call == null) {
		throw new NullPointerException("Call.Factory returned null.");
	}
	return call;
}

真正的okhttp3.Call來自于serviceMethod.callFactory.newCall(request),那么serviceMethod.callFactory又是從哪里來的呢。打開ServiceMethod<T>這個類,在構造函數(shù)中有如下代碼:

this.callFactory = builder.retrofit.callFactory();

說明這個callFactory來自于retrofit.callFactory(),進一步查看Retrofit類的源碼:

okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
    callFactory = new OkHttpClient();
}

在通過Retrofit.Builder創(chuàng)建retrofit實例的時候,可以通過下面的方法設置factory實例,如果不設置,默認會創(chuàng)建一個OkHttpClient。

public Builder callFactory(okhttp3.Call.Factory factory) {
    this.callFactory = checkNotNull(factory, "factory == null");
    return this;
}

到這里所有的脈絡都清楚了,如果創(chuàng)建Retrofit實例時,設置我們自己的callFactory,在該factory中,調(diào)用的call.enqueue將根據(jù)設置的response直接調(diào)用callback中的onResponse或者onFailure方法,從而回避掉異步的問題。具體的實現(xiàn)代碼如下:

public class MockFactory extends OkHttpClient {

    private MockCall mockCall;

    public MockFactory() {
        mockCall = new MockCall();
    }

    public void mockResponse(Response.Builder mockBuilder) {
        mockCall.setResponseBuilder(mockBuilder);
    }

    @Override
    public Call newCall(Request request) {
        mockCall.setRequest(request);
        return mockCall;
    }

    public class MockCall implements Call {
        // Guarded by this.
        private boolean executed;
        volatile boolean canceled;

        /** The application's original request unadulterated by redirects or auth headers. */
        Request originalRequest;
        Response.Builder mockResponseBuilder;
        HttpEngine engine;

        protected MockCall() {}

//        protected MockCall(Request originalRequest, boolean mockFailure,
//                           Response.Builder mockResponseBuilder) {
//            this.originalRequest = originalRequest;
//            this.mockFailure = mockFailure;
//            this.mockResponseBuilder = mockResponseBuilder;
//            this.mockResponseBuilder.request(originalRequest);
//        }

        public void setRequest(Request originalRequest) {
            this.originalRequest = originalRequest;
        }

        public void setResponseBuilder(Response.Builder mockResponseBuilder) {
            this.mockResponseBuilder = mockResponseBuilder;
        }

        @Override
        public Request request() {
            return originalRequest;
        }

        @Override
        public Response execute() throws IOException {
            return mockResponseBuilder.request(originalRequest).build();
        }

        @Override
        public void enqueue(Callback responseCallback) {
            synchronized (this) {
                if (executed) throw new IllegalStateException("Already Executed");
                executed = true;
            }

            int code = mockResponseBuilder.request(originalRequest).build().code();
            if (code >= 200 && code < 300) {
                try {
                    if (mockResponseBuilder != null) {
                        responseCallback.onResponse(this,
                                mockResponseBuilder.build());
                    }
                } catch (IOException e) {
                    // Nothing
                }
            } else {
                responseCallback.onFailure(this, new IOException("Mock responseCallback onFailure"));
            }
        }

        @Override
        public void cancel() {
            canceled = true;
            if (engine != null) engine.cancel();
        }

        @Override
        public synchronized boolean isExecuted() {
            return executed;
        }

        @Override
        public boolean isCanceled() {
            return canceled;
        }
    }
}

下面看下單元測試的時候怎么用。

1)通過反射或者mock,修改被測代碼中的retrofit實例,調(diào)用callFactory來設置上面的MockFactory

2)準備好要返回的response,設置MockFactory的mockResponse,調(diào)用被測方法,校驗結果

@Test
public void testxxx() throws Exception {
    ResponseBody responseBody = ResponseBody.create(MediaType.parse("application/json"),
            RestServiceTestHelper.getStringFromFile("xxx.json"));
    Response.Builder mockBuilder = new Response.Builder()
            .addHeader("Content-Type", "application/json")
            .protocol(Protocol.HTTP_1_1)
            .code(200)
            .body(responseBody);
    mMockFactory.mockResponse(mockBuilder);

    // call the method to be tested
// verfify if the result is expected
}

參考文獻:

1. robolectric.org

2. https://square.github.io/retrofit/

3. http://stackoverflow.com/questions/37909276/testing-retrofit-2-with-robolectric-callbacks-not-being-called

4. https://riggaroo.co.za/retrofit-2-mocking-http-responses/

新聞標題:Roboletric+Retrofit2單元測試
文章來源:http://chinadenli.net/article32/podopc.html

成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供ChatGPT、網(wǎng)站排名定制開發(fā)、外貿(mào)網(wǎng)站建設、品牌網(wǎng)站建設營銷型網(wǎng)站建設

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

h5響應式網(wǎng)站建設