I want to do an unit test that verifies if function1() or function2() were called. I haven't work with callbacks before, can you give me any idea about how to do it?
public void sendData(HttpService service, Document userData) {
Call<String> call = service.updateDocument(getId(), userData);
call.enqueue(new Callback<String>() {
#Override
public void onResponse(Call<String> call, Response<String> response) {
function1(response.code());
}
#Override
public void onFailure(Call<String> call, Throwable t) {
function2();
}
});
}
I couldn't try, but it should work. Maybe you have to fix generic type
casting errors like mock(Call.class);.
#Test
public void should_test_on_response(){
Call<String> onResponseCall = mock(Call.class);
doAnswer(invocation -> {
Response response = null;
invocation.getArgumentAt(0, Callback.class).onResponse(onResponseCall, response);
return null;
}).when(onResponseCall).enqueue(any(Callback.class));
sendData(....);
// verify function1
}
#Test
public void should_test_on_failure(){
Call<String> onResponseCall = mock(Call.class);
doAnswer(invocation -> {
Exception ex = new RuntimeException();
invocation.getArgumentAt(0, Callback.class).onFailure(onResponseCall, ex);
return null;
}).when(onResponseCall).enqueue(any(Callback.class));
sendData(....);
// verify function2
}
Related
I am a new with Rxjava. I want to capture access token (received as part of headers) from the response of the first api call and then use that access token (as header) for subsequent api calls.
Note: the in the first api call, the response has no body,only headers are returned.
My Network Interface:
public interface NetworkInterface {
#POST("https://appauth.treuspan/Login")
Observable<Response<Void>> getAuthDetails(
#Header("Content-Type") String contentType,
#Body AuthRequestDetails authRequestDetails
);
#POST("https://appauth.treuspan//storedetails")
Observable<StoreDetailsResponse> getStoreDetails(
#Header("Content-Type") String contentType,
#Header("Accept") String accept,
#Header("Authorization") String authorization,
#Body AuthRequestBody authRequestBody
);
}
My observable methods:
public Observable<Response<Void>> getAuthObservable() {
return NetworkClient.getRetrofit().create(NetworkInterface.class)
.getAuthDetails("application/x-www-form-urlencoded",
new AuthRequestDetails().getAuthRequestDetails())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Response<Void>>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(Response<Void> voidResponse) {
String access_token_received = voidResponse.headers().get("access_token");
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
});
The above doesn't compile. Its compile says:
Incompatible type:
required: io.reactivex.Observable<retrofit2.Response<java.lang.Void>>
Found: void
If I am able to successfully make the above call then i have to use the access token as part of header in the subsequent call as follows
public Observable<StoreDetailsResponse> getStoreDetailsObservable() {
return NetworkClient.getRetrofit().create(NetworkInterface.class)
.getStoreDetails("application/x-www-form-urlencoded",
"application/json",
"Bearer access_token_received",
new AuthRequestBody().getAuthRequestBody())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<StoreDetailsResponse>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(StoreDetailsResponse response) {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
});
}
The above code also doesn't compile. It says:
Incompatible type:
required: io.reactivex.Observable<com.example.androidtest.model.StoreDetailsResponse>
Found: void
Please suggest how can i achieve this.
subscribe(...) will return a Subscription
Try to edit your code, make method return a Subscription like this
public Subscription getAuthObservable() {
return NetworkClient.getRetrofit().create(NetworkInterface.class)
.getAuthDetails("application/x-www-form-urlencoded",
new AuthRequestDetails().getAuthRequestDetails())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Response<Void>>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(Response<Void> voidResponse) {
String access_token_received = voidResponse.headers().get("access_token");
}
});
}
I want to run changesHasBeenSaved if saveChanges function gives me true value.
How can I get boolean or string or integer, any value, when Retrofit finish?
Is it possible?
I need a function similar this:
public boolean saveChanges()
{
Boolean output = false;
RequestAPI requestAPI = Requests.getRetrofit();
Call<Object> jsonObjectCall = requestAPI.readAllCategoeies();
jsonObjectCall.enqueue(new Callback<Object>() {
#Override
public void onResponse(Call<Object> call, Response<Object> response) {
if(response.body() != null) {
output = true;
}
}
#Override
public void onFailure(Call<Object> call, Throwable t) {
call.cancel();
}
});
return output;
}
You should change method return type boolean to void as retrofit work asynchronous.
You should pass the listener/callback to get the status in callback.
First create callback interface like below
public interface ApiCallback {
void onResponse(boolean success);
}
Here how saveChanges will look like
public void saveChanges(final ApiCallback callback)
{
RequestAPI requestAPI = Requests.getRetrofit();
Call<Object> jsonObjectCall = requestAPI.readAllCategoeies();
jsonObjectCall.enqueue(new Callback<Object>() {
#Override
public void onResponse(Call<Object> call, Response<Object> response) {
callback.onResponse(response.body() != null);
}
#Override
public void onFailure(Call<Object> call, Throwable t) {
call.cancel();
callback.onResponse(false);
}
});
}
Then you need to call saveChanges method like below
saveChanges(new ApiCallback () {
public void onResponse(boolean success){
if(success){
// do something
} else{
// do something
}
}
});
jsonObjectCall.enqueue(new Callback<Object>() {
#Override
public void onResponse(Call<Object> call, Response<Object> response) {
//when the response is successful
if(response.isSuccessful()) {
output = true;
// display response as string
Log.i(TAG, "post submitted to API." + response.body().toString());
//Object user1 = response.body(); maps response to the modal class
}
}
#Override
public void onFailure(Call<Object> call, Throwable t) {
call.cancel();
}
})
Change you call back to like the above , included comments for clearity about usage .
Hope this helps.
com.squareup.retrofit2:retrofit:2.3.0
My steps:
Start async request: getCompaniesList()
Wait success response
Start another async request: getCatalogsList()
Wait success response
Do some another code
Here snippet in my activity:
RestClient restClient = RestClientFactory.getRestClient();
Call<List<Company>> companyList = restClient.getCompaniesList(filters);
companyList.enqueue(new Callback<List<Company>>() {
#Override
public void onResponse(Call<List<Company>> call, Response<List<Company>> response) {
if (response.isSuccessful()) {
RestClient restClient = RestClientFactory.getRestClient();
Call<List<Catalog>> catalogList = restClient.getCatalogsList(filters);
catalogList.enqueue(new Callback<List<Catalog>>() {
#Override
public void onResponse(Call<List<Catalog>> call, Response<List<Catalog>> response) {
if (response.isSuccessful()) {
// HERE SOME NEED CODE!!!
}
}
#Override
public void onFailure(Call<List<Catalog>> call, Throwable throwable) {
}
});
}
}
#Override
public void onFailure(Call<List<Company>> call, Throwable throwable) {
}
});
I think this structure is not very nice. Тoo many nested items. As result code is more complicated.
Question: Has any alternative to this structure?
You can either take time to learn Rx RXjava2
or you can split up your code like this
RestClient restClient = RestClientFactory.getRestClient();
Call<List<Company>> companyList = restClient.getCompaniesList(filters);
companyList.enqueue(getCompanyListCallback());
private Callback<List<Company>> getCompanyListCallback() {
return new Callback<List<Company>>() {
#Override
public void onResponse(Call<List<Company>> call, Response<List<Company>> response) {
if (response.isSuccessful()) {
RestClient restClient = RestClientFactory.getRestClient();
Call<List<Catalog>> catalogList = restClient.getCatalogsList(filters);
catalogList.enqueue(getCatalogsListCallback());
}
}
#Override
public void onFailure(Call<List<Company>> call, Throwable throwable) {
}
};
}
private Callback<List<Catalog>> getCatalogsListCallback() {
return new Callback<List<Catalog>>() {
#Override
public void onResponse(Call<List<Catalog>> call, Response<List<Catalog>> response) {
if (response.isSuccessful()) {
// HERE SOME NEED CODE!!!
}
}
#Override
public void onFailure(Call<List<Catalog>> call, Throwable throwable) {
}
};
}
hi i am using retrofit my callback is as follow
#Override
public void onResponse(final Call<T> call, Response<T> response) {
if (response.isSuccessful()) {
passing this to my view
} else {
// as this failed other then 200 retroCallback.onFailure(call, new Throwable(""));
}
}
#Override
public void onFailure(Call<T> call, Throwable t) {
retroCallback.onFailure(call, t);
}
so in this how i can pass my ErrorBean instead of Throwable anyway we can pass custom model in onFailure ? as my server giving me response in some formate i want to pass that format .. i am using retrofit 2.1.0
You can subclass Throwable and pass additional object using composition.
public class ErrorBean extends Throwable {
public ErrorPayload payload = null;
public ErrorBean(ErrorPayload payload) {
this.payload = payload;
}
}
Then, in onError:
#Override
public void onFailure(Call<T> call, Throwable t) {
retroCallback.onFailure(call, t);
if (t instanceof ErrorBean) {
// do your stuff here
((ErrorBean)t).payload.text;
}
}
AFAIK,, Retrofit's onFailure is used for handling errors like no internet connection.
To handle the error response from your Server, Error response, I mean response from Server with 4xx status code but with some JSON response for client to handle it.
Say, you are getting this error structure from Server:
{
statusCode: 409,
message: "Email address already registered"
}
This error will be captured in onResponse(...). To handle this, create your
public class ErrorBean {
private int statusCode;
private String message;
public ErrorBean() {
}
public int status() {
return statusCode;
}
public String message() {
return message;
}
}
Create a simple ErrorHandler util:
public class ErrorUtils {
public static ErrorBean parseError(Response<?> response) {
Converter<ResponseBody, ErrorBean> converter =
ServiceGenerator.retrofit()
.responseBodyConverter(ErrorBean.class, new Annotation[0]);
ErrorBean error;
try {
error = converter.convert(response.errorBody());
} catch (IOException e) {
return new ErrorBean();
}
return error;
}
}
And finally,
...
call.enqueue(new Callback<SuccessResponse>() {
#Override
public void onResponse(Call<SuccessResponse> call, Response<SuccessResponse> response) {
if (response.isSuccessful()) {
// use response data and do some fancy stuff :)
} else {
// parse the response body …
ErrorBean error = ErrorUtils.parseError(response);
// … and use it to show error information
// … or just log the issue like we’re doing :)
Log.d("error message", error.message());
}
}
#Override
public void onFailure(Call<User> call, Throwable t) {
// there is more than just a failing request (like: no internet connection)
}
});
Hope you got the point..!!!
I have created a helper class to handle all of my http calls in my app. It is a simple singleton wrapper for okhttp that looks like this (I have omitted some unimportant parts):
public class HttpUtil {
private OkHttpClient client;
private Request.Builder builder;
...
public void get(String url, HttpCallback cb) {
call("GET", url, cb);
}
public void post(String url, HttpCallback cb) {
call("POST", url, cb);
}
private void call(String method, String url, final HttpCallback cb) {
Request request = builder.url(url).method(method, method.equals("GET") ? null : new RequestBody() {
// don't care much about request body
#Override
public MediaType contentType() {
return null;
}
#Override
public void writeTo(BufferedSink sink) throws IOException {
}
}).build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Request request, Throwable throwable) {
cb.onFailure(null, throwable);
}
#Override
public void onResponse(Response response) throws IOException {
if (!response.isSuccessful()) {
cb.onFailure(response, null);
return;
}
cb.onSuccess(response);
}
});
}
public interface HttpCallback {
/**
* called when the server response was not 2xx or when an exception was thrown in the process
* #param response - in case of server error (4xx, 5xx) this contains the server response
* in case of IO exception this is null
* #param throwable - contains the exception. in case of server error (4xx, 5xx) this is null
*/
public void onFailure(Response response, Throwable throwable);
/**
* contains the server response
* #param response
*/
public void onSuccess(Response response);
}
}
Then, in my main activity, I use this helper class :
HttpUtil.get(url, new HttpUtil.HttpCallback() {
#Override
public void onFailure(Response response, Throwable throwable) {
// handle failure
}
#Override
public void onSuccess(Response response) {
// <-------- Do some view manipulation here
}
});
onSuccess throws an exception when the code runs :
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the
original thread that created a view hierarchy can touch its views.
From my understanding, Okhttp callbacks run on the main thread so why do I get this error ?
** Just as a side note, I have created HttpCallback interface to wrap Okhttp's Callback class because I wanted to change the behaviour of onResponse and onFailure so I could unite the logic of handling failed responses due to i/o exception and failed responses due to server problems.
Thanks.
From my understanding, Okhttp callbacks run on the main thread so why do I get this error ?
This is not true. Callbacks run on a background thread. If you want to immediately process something in the UI you will need to post to the main thread.
Since you already have a wrapper around the callback you can do this internally in your helper so that all HttpCallback methods are invoked on the main thread for convenience.
As Jake Wharton suggested, I had to run the callbacks on the main thread explicitly.
So I wrapped the calls to the callbacks with Runnable like this:
private void call(String method, String url, final HttpCallback cb) {
...
client.newCall(request).enqueue(new Callback() {
Handler mainHandler = new Handler(context.getMainLooper());
#Override
public void onFailure(Request request,final Throwable throwable) {
mainHandler.post(new Runnable() {
#Override
public void run() {
cb.onFailure(null, throwable);
}
});
}
#Override
public void onResponse(final Response response) throws IOException {
mainHandler.post(new Runnable() {
#Override
public void run() {
if (!response.isSuccessful()) {
cb.onFailure(response, null);
return;
}
cb.onSuccess(response);
}
});
}
});
}
I know it's an old question, but recently I encountered the same issue. If you need to update any view, you will need to use runOnUiThread() or post the result back on the main thread.
HttpUtil.get(url, new Callback() { //okhttp3.Callback
#Override
public void onFailure(Call call, IOException e) { /* Handle error **/ }
#Override
public void onResponse(Call call, Response response) throws IOException {
String myResponse = response.body().string();
//Do something with response
//...
MyActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
//Handle UI here
findViewById(R.id.loading).setVisibility(View.GONE);
}
});
}
});
According to Retrofit documentation Callback methods are executed on UI thread by default until you provide a callback executor to Retrofit OR when using custom method return types using CallAdapterFactory
Michael's above response is a pretty good solution for having your callback happen in a calling Activity and I used it as a guide for my solution that I will outline below (solution in Kotlin);
Interface for HttpCallback
interface HttpCallback {
fun onResponse(response: Response)
fun onFailure(call: Call, e: IOException)
}
Implementation of the Interface. I pass the calling activity as a weak reference to perform UI updates.
class ImageUploadCallback(activity: WeakReference<Activity>) : HttpCallback {
val _activity = activity
override fun onResponse(response: Response) {
if(_activity.get() != null){
val activty = _activity.get()!!
activty.runOnUiThread(Runnable {
//Update the UI to your hearts content
});
}
}
override fun onFailure(call: Call, e: IOException) {
//Your implemtnation here
}
}
Calling HttpProxy class from the activity, passing the callback as a parameter
HttpProxy.UploadImage(
imageToUpload.name,
imageToUpload,
MEDIA_TYPE!!,
//our callback
ImageUploadCallback(WeakReference(this))
And finally, the code in the HttpProxy class that takes in the callback;
fun UploadImage(filename: String, sourceImageFile: File, mediaType: MediaType, callback: HttpCallback) {
//building up our request...and then calling
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
callback.onFailure(call, e)
}
override fun onResponse(call: Call, response: Response) {
response.use {
try{
callback.onResponse(response)
}
catch(e: Exception){
}
}
}
});
}
What i would do is extend okhttp callback and call failure / response on main thread so caller don't have to care about posting them on main thread.
Call call;
call.enqueue(new OkHttpCallbackOnMain() {
#Override
void onCallFailure(Call call, IOException e) {
// update UI
}
#Override
void onCallResponse(Call call, Response response) {
// update UI
}
});
abstract class OkHttpCallbackOnMain implements Callback {
abstract void onCallFailure(Call call, IOException e);
abstract void onCallResponse(Call call, Response response));
#Override
public void onFailure(#NonNull Call call, #NonNull IOException e) {
getMainHandler().post(() -> onCallFailure(call, e));
}
#Override
public void onResponse(#NonNull Call call, #NonNull Response response) {
getMainHandler().post(() -> onCallResponse(call, response));
}
private Handler getMainHandler() {
return new Handler(Looper.getMainLooper());
}
}