studio programacion para móviles libro edición desarrollo desarrollar curso aprende aplicaciones android testing mocking retrofit

android - programacion - El servidor de modificación cuadrada se simula para probar



manual de programacion android pdf (9)

¿Cuál es la mejor manera de burlarse de un servidor para probar al usar el marco de actualización cuadrada ?

Posibles formas:

  1. Cree un nuevo client actualización y configúrelo en RestAdapter.Builder (). SetClient (). Esto implica analizar el objeto Request y devolver el json como un objeto Response.

  2. Implemente esta interfaz anotada como clase simulada y utilícela en lugar de la versión proporcionada por RestAdapter.create () (no probará la serialización gson)

  3. ?

Idealmente, quiero que el servidor simulado proporcione respuestas json para que pueda probar la serialización gson al mismo tiempo.

Cualquier ejemplo sería muy apreciado.


Mock Retrofit 2.0 solicitudes de prueba

Como los viejos mecanismos como la creación de la clase MockClient y su implementación desde el Client ya no funcionan con Retrofit 2.0, aquí describo una nueva forma de hacerlo. Todo lo que necesita hacer ahora es agregar sus interceptores personalizados para OkHttpClient como se muestra a continuación . FakeInterceptor clase FakeInterceptor simplemente anula el método de intercept y, en el caso de que la aplicación esté en modo DEBUG , devuelve JSON dado.

RestClient.java

public final class RestClient { private static IRestService mRestService = null; public static IRestService getClient() { if(mRestService == null) { final OkHttpClient client = new OkHttpClient(); // ***YOUR CUSTOM INTERCEPTOR GOES HERE*** client.interceptors().add(new FakeInterceptor()); final Retrofit retrofit = new Retrofit.Builder() // Using custom Jackson Converter to parse JSON // Add dependencies: // com.squareup.retrofit:converter-jackson:2.0.0-beta2 .addConverterFactory(JacksonConverterFactory.create()) // Endpoint .baseUrl(IRestService.ENDPOINT) .client(client) .build(); mRestService = retrofit.create(IRestService.class); } return mRestService; } }

IRestService.java

public interface IRestService { String ENDPOINT = "http://www.vavian.com/"; @GET("/") Call<Teacher> getTeacherById(@Query("id") final String id); }

FakeInterceptor.java

public class FakeInterceptor implements Interceptor { // FAKE RESPONSES. private final static String TEACHER_ID_1 = "{/"id/":1,/"age/":28,/"name/":/"Victor Apoyan/"}"; private final static String TEACHER_ID_2 = "{/"id/":1,/"age/":16,/"name/":/"Tovmas Apoyan/"}"; @Override public Response intercept(Chain chain) throws IOException { Response response = null; if(BuildConfig.DEBUG) { String responseString; // Get Request URI. final URI uri = chain.request().url().uri(); // Get Query String. final String query = uri.getQuery(); // Parse the Query String. final String[] parsedQuery = query.split("="); if(parsedQuery[0].equalsIgnoreCase("id") && parsedQuery[1].equalsIgnoreCase("1")) { responseString = TEACHER_ID_1; } else if(parsedQuery[0].equalsIgnoreCase("id") && parsedQuery[1].equalsIgnoreCase("2")){ responseString = TEACHER_ID_2; } else { responseString = ""; } response = new Response.Builder() .code(200) .message(responseString) .request(chain.request()) .protocol(Protocol.HTTP_1_0) .body(ResponseBody.create(MediaType.parse("application/json"), responseString.getBytes())) .addHeader("content-type", "application/json") .build(); } else { response = chain.proceed(chain.request()); } return response; } }

Código fuente del proyecto en GitHub


  1. Primero, crea tu interfaz de Retrofit.

    public interface LifeKitServerService { /** * query event list from server,convert Retrofit''s Call to RxJava''s Observerable * * @return Observable<HttpResult<List<Event>>> event list from server,and it has been convert to Obseverable */ @GET("api/event") Observable<HttpResult<List<Event>>> getEventList(); }

  2. Su Solicitante de la siguiente manera:

    public final class HomeDataRequester { public static final String TAG = HomeDataRequester.class.getSimpleName(); public static final String SERVER_ADDRESS = BuildConfig.DATA_SERVER_ADDR + "/"; private LifeKitServerService mServerService; private HomeDataRequester() { OkHttpClient okHttpClient = new OkHttpClient.Builder() //using okhttp3 interceptor fake response. .addInterceptor(new MockHomeDataInterceptor()) .build(); Retrofit retrofit = new Retrofit.Builder() .client(okHttpClient) .baseUrl(SERVER_ADDRESS) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .addConverterFactory(GsonConverterFactory.create(new Gson())) .build(); //using okhttp3 inteception to fake response. mServerService = retrofit.create(LifeKitServerService.class); //Second choice,use MockRetrofit to fake data. //NetworkBehavior behavior = NetworkBehavior.create(); //MockRetrofit mockRetrofit = new MockRetrofit.Builder(retrofit) // .networkBehavior(behavior) // .build(); //mServerService = new MockLifeKitServerService( // mockRetrofit.create(LifeKitServerService.class)); } public static HomeDataRequester getInstance() { return InstanceHolder.sInstance; } public void getEventList(Subscriber<HttpResult<List<Event>>> subscriber) { mServerService.getEventList() .subscribeOn(Schedulers.io()) .unsubscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(subscriber); } }

  3. Si está utilizando la segunda opción (use la interfaz Retrofit para simular los datos del servidor), necesita MockRetrofit, use el código siguiente:

    public final class MockLifeKitServerService implements LifeKitServerService { public static final String TAG = MockLifeKitServerService.class.getSimpleName(); private BehaviorDelegate<LifeKitServerService> mDelegate; private Gson mGson = new Gson(); public MockLifeKitServerService(BehaviorDelegate<LifeKitServerService> delegate) { mDelegate = delegate; } @Override public Observable<HttpResult<List<Event>>> getEventList() { List<Event> eventList = MockDataGenerator.generateEventList(); HttpResult<List<Event>> httpResult = new HttpResult<>(); httpResult.setCode(200); httpResult.setData(eventList); LogUtil.json(TAG, mGson.toJson(httpResult)); String text = MockDataGenerator.getMockDataFromJsonFile("server/EventList.json"); if (TextUtils.isEmpty(text)) { text = mGson.toJson(httpResult); } LogUtil.d(TAG, "Text:/n" + text); text = mGson.toJson(httpResult); return mDelegate.returningResponse(text).getEventList(); }

4.Mi datos provienen del archivo de activos (Asset / server / EventList.json), el contenido de este archivo es:

{ "code": 200, "data": [ { "uuid": "e4beb3c8-3468-11e6-a07d-005056a05722", "title": "title", "image": "http://image.jpg", "goal": 1500000, "current": 51233, "hot": true, "completed": false, "createdAt": "2016-06-15T04:00:00.000Z" } ] }

5.Si está utilizando un interceptor okhttp3, necesita un interceptor autodefinido, como este:

public final class MockHomeDataInterceptor implements Interceptor { public static final String TAG = MockHomeDataInterceptor.class.getSimpleName(); @Override public Response intercept(Chain chain) throws IOException { Response response = null; String path = chain.request().url().uri().getPath(); LogUtil.d(TAG, "intercept: path=" + path); response = interceptRequestWhenDebug(chain, path); if (null == response) { LogUtil.i(TAG, "intercept: null == response"); response = chain.proceed(chain.request()); } return response; } private Response interceptRequestWhenDebug(Chain chain, String path) { Response response = null; if (BuildConfig.DEBUG) { Request request = chain.request(); if (path.equalsIgnoreCase("/api/event")) { //get event list response = getMockEventListResponse(request); } } private Response getMockEventListResponse(Request request) { Response response; String data = MockDataGenerator.getMockDataFromJsonFile("server/EventList.json"); response = getHttpSuccessResponse(request, data); return response; } private Response getHttpSuccessResponse(Request request, String dataJson) { Response response; if (TextUtils.isEmpty(dataJson)) { LogUtil.w(TAG, "getHttpSuccessResponse: dataJson is empty!"); response = new Response.Builder() .code(500) .protocol(Protocol.HTTP_1_0) .request(request) //protocol&request be set,otherwise will be exception. .build(); } else { response = new Response.Builder() .code(200) .message(dataJson) .request(request) .protocol(Protocol.HTTP_1_0) .addHeader("Content-Type", "application/json") .body(ResponseBody.create(MediaType.parse("application/json"), dataJson)) .build(); } return response; } }

6.Por último, puede solicitar su servidor con código:

mHomeDataRequester.getEventList(new Subscriber<HttpResult<List<Event>>>() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { LogUtil.e(TAG, "onError: ", e); if (mView != null) { mView.onEventListLoadFailed(); } } @Override public void onNext(HttpResult<List<Event>> httpResult) { //Your json result will be convert by Gson and return in here!!! }); }

Gracias por leer.



Agregando a la respuesta de @Alec, extendí el cliente simulado para obtener la respuesta directamente de un archivo de texto en la carpeta de activos dependiendo de la URL de la solicitud.

Ex

@POST("/activate") public void activate(@Body Request reqdata, Callback callback);

Aquí el cliente simulado entiende que la URL que se está disparando es activa y busca un archivo llamado activate.txt en la carpeta de activos. Lee el contenido del archivo assets / activate.txt y lo envía como respuesta para la API.

Aquí está el MockClient extendido

public class MockClient implements Client { Context context; MockClient(Context context) { this.context = context; } @Override public Response execute(Request request) throws IOException { Uri uri = Uri.parse(request.getUrl()); Log.d("MOCK SERVER", "fetching uri: " + uri.toString()); String filename = uri.getPath(); filename = filename.substring(filename.lastIndexOf(''/'') + 1).split("?")[0]; try { Thread.sleep(2500); } catch (InterruptedException e) { e.printStackTrace(); } InputStream is = context.getAssets().open(filename.toLowerCase() + ".txt"); int size = is.available(); byte[] buffer = new byte[size]; is.read(buffer); is.close(); String responseString = new String(buffer); return new Response(request.getUrl(), 200, "nothing", Collections.EMPTY_LIST, new TypedByteArray("application/json", responseString.getBytes())); } }

Para una explicación detallada, puedes consultar mi blog
http://www.cumulations.com/blogs/13/Mock-API-response-in-Retrofit-using-custom-clients


Decidí probar el método 1 de la siguiente manera

public class MockClient implements Client { @Override public Response execute(Request request) throws IOException { Uri uri = Uri.parse(request.getUrl()); Log.d("MOCK SERVER", "fetching uri: " + uri.toString()); String responseString = ""; if(uri.getPath().equals("/path/of/interest")) { responseString = "JSON STRING HERE"; } else { responseString = "OTHER JSON RESPONSE STRING"; } return new Response(request.getUrl(), 200, "nothing", Collections.EMPTY_LIST, new TypedByteArray("application/json", responseString.getBytes())); } }

Y usándolo por:

RestAdapter.Builder builder = new RestAdapter.Builder(); builder.setClient(new MockClient());

¡Funciona bien y le permite probar sus cadenas json sin tener que contactar al servidor real!


Para mí, el Retrofit Client personalizado es excelente por su flexibilidad. Especialmente cuando usas cualquier marco de DI, puedes simular activar / desactivar rápida y fácilmente. Estoy usando Cliente personalizado provisto por Dagger también en unidades y pruebas de integración.

Editar: Aquí encontrará ejemplos de modificaciones falsas https://github.com/pawelByszewski/retrofitmock


Probando la deserialización de JSON a sus objetos (presumiblemente con TypeAdapters ?) Parece un problema separado que requiere pruebas unitarias por separado.

Yo uso la versión 2 personalmente. Ofrece código seguro para el tipo de refactor, que se puede depurar y modificar fácilmente. Después de todo, ¿de qué sirve declarar su API como interfaces si no está creando versiones alternativas para probarlas? Polimorfismo para ganar

Otra opción es usar un Proxy Java. Así es como Retrofit (actualmente) implementa su interacción HTTP subyacente. Sin duda, esto requerirá más trabajo, pero permitiría muchos más simulacros dinámicos.


Soy un gran admirador de Apiary.io por burlarse de una API antes de pasar a un servidor real.

También puede usar archivos planos .json y leerlos desde el sistema de archivos.

También puede usar API de acceso público como Twitter, Flickr, etc.

Aquí hay otros recursos geniales sobre Retrofit.

Presentaciones: https://docs.google.com/presentation/d/12Eb8OPI0PDisCjWne9-0qlXvp_-R4HmqVCjigOIgwfY/edit#slide=id.p

Video: http://www.youtube.com/watch?v=UtM06W51pPw&feature=g-user-u

Proyecto de ejemplo: https://github.com/dustin-graham/ucad_twitter_retrofit_sample


Mockery (descargo de responsabilidad: yo soy el autor) fue diseñado solo para esta tarea exacta.

Mockery es una biblioteca de burlas / pruebas centrada en la validación de capas de red con soporte integrado para Retrofit. Genera automáticamente pruebas JUnit basadas en las especificaciones de una Api determinada. La idea es no tener que escribir manualmente ninguna prueba; ni implementando interfaces para burlarse de las respuestas del servidor.