广场改造服务器模拟testing
在使用方形改进框架时,嘲笑服务器进行testing的最好方法是什么?
潜在的方法:
-
创build一个新的翻新客户端并将其设置在RestAdapter.Builder()。setClient()中。 这涉及parsingRequest对象并将Json作为Response对象返回。
-
实现这个带注释的接口作为模拟类,并使用它来代替RestAdapter.create()提供的版本(不会testinggson序列化)
-
?
理想情况下,我想让模拟的服务器提供json响应,所以我可以同时testinggson序列化。
任何例子将不胜感激。
我决定尝试如下方法1
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())); } }
通过以下方式使用它:
RestAdapter.Builder builder = new RestAdapter.Builder(); builder.setClient(new MockClient());
它运行良好,并允许您testing您的JSONstring,而无需联系真正的服务器!
模拟改造2.0请求testing
由于旧的机制,如创buildMockClient
类,并从Client
实施它不再与改造2.0,在这里我描述了一个新的方式做到这一点。 所有你现在要做的是为OkHttpClient添加自定义拦截器,如下所示 。 FakeInterceptor
类只是重写intercept
方法,如果应用程序处于DEBUG
模式,则返回给定的JSON。
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().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; } }
GitHub上的项目源代码
testingJSON反序列化到你的对象(推测与TypeAdapters
?)似乎是一个单独的问题,需要单独的unit testing。
我个人使用版本2。 它提供了types安全,重构友好的代码,可以很容易地debugging和修改。 毕竟,如果您没有为testing创build替代版本,那么将API声明为接口有什么好处呢! 多态性的胜利。
另一种select是使用Java Proxy
。 这实际上是Retrofit(当前)如何实现其基础的HTTP交互。 这将承认需要更多的工作,但会允许更多的dynamic模拟。
在转移到真正的服务器之前,我是Apiary.io的忠实粉丝,嘲笑API。
您也可以使用flat .json文件并从文件系统读取它们。
你也可以使用可公开访问的API,如Twitter,Flickr等。
这里有一些关于Retrofit的其他很好的资源。
幻灯片: 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
示例项目: https : //github.com/dustin-graham/ucad_twitter_retrofit_sample
你也可以使用Squareup的Webservermock之类的东西! – > https://github.com/square/okhttp/tree/master/mockwebserver
-
首先,创build您的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(); }
-
您的申请人如下:
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); } }
-
如果使用第二种select(使用Retrofit接口来模拟服务器数据),则需要MockRetrofit,使用代码如下:
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.我的数据来自资产文件(Asset / server / EventList.json),这个文件的内容是:
{ "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.如果您使用的是okhttp3拦截器,则需要自定义拦截器,如下所示:
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.最后,你可以用代码请求你的服务器:
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!!! }); }
谢谢阅读。
通过@Alec添加答案,我已经扩展了模拟客户端,以根据请求URL直接从资产文件夹中的文本文件获取响应。
防爆
@POST("/activate") public void activate(@Body Request reqdata, Callback callback);
在这里模拟客户端,明白被激发的URL是激活的,并在资产文件夹中查找名为activate.txt的文件。 它从assets / activate.txt文件中读取内容,并将其作为API的响应发送。
这里是扩展的MockClient
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())); } }
有关详细的解释,你可以检查我的博客
http://www.cumulations.com/blogs/13/Mock-API-response-in-Retrofit-using-custom-clients
嘲弄 (免责声明:我是作者)是专门为此目的而devise的。
嘲笑是一个模拟/testing图书馆,侧重于validationnetworking层与内置支持改造。 它根据给定的Api的规格自动生成JUnittesting。 这个想法是不必手动写任何testing; 既没有实现模拟服务器响应的接口。
对我来说,自定义的改造客户端是非常好的,因为它具有灵活性。 特别是当你使用任何DI框架,你可以快速,简单地打开/closures模拟。 我正在使用由Dagger提供的客户端,也在单元和集成testing中。
编辑:在这里你find嘲笑改造的例子https://github.com/pawelByszewski/retrofitmock