Square retrofit server mock for testing

前端 未结 11 1346
栀梦
栀梦 2020-12-07 06:54

What\'s the best way to mock a server for testing when using the square retrofit framework.

Potential ways:

  1. Create a new retrofit client and set it

相关标签:
11条回答
  • 2020-12-07 07:24
    1. First,create your Retrofit interface.

      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. Your Requester in follow:

      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. If you using the second choice(use Retrofit interface to Mock server data),you need to MockRetrofit,use code follow:

      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.My data is from asset file(Asset/server/EventList.json),this file content is:

        {
          "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.If you are using okhttp3 interceptor,you need to self-defined interceptor,like this:

    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.Finally,you can request your server with code:

    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!!!
        });
    }
    

    Thanks for reading.

    0 讨论(0)
  • 2020-12-07 07:29

    I am a big fan of Apiary.io for a mocking an API before moving to a real server.

    You could also use flat .json files and read them from the file system.

    You could also use publicly accessible API's like Twitter, Flickr, etc.

    Here are some other great resources about Retrofit.

    Slides: 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

    Example Project: https://github.com/dustin-graham/ucad_twitter_retrofit_sample

    0 讨论(0)
  • 2020-12-07 07:31

    Mockery (disclaimer: I’m the author) was designed just for this exactly task.

    Mockery is a mocking/testing library focused on validating networking layers with built-in support for Retrofit. It auto-generates JUnit tests based on the specs of a given Api. The idea is not to have to write manually any test; neither implementing interfaces for mocking server responses.

    0 讨论(0)
  • 2020-12-07 07:33

    Mock Retrofit 2.0 Requests for Testing

    As the old mechanisms like creating MockClient class and implementing it from Client are not working anymore with Retrofit 2.0, here I describe a new way of doing that. All what you need to do now is to add your custom interceptors for OkHttpClient like it is shown below. FakeInterceptor class just overrides intercept method and in the case if application is in DEBUG mode return given 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().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;
        }
    }
    

    Source code of project on GitHub

    0 讨论(0)
  • 2020-12-07 07:35

    I decided to try method 1 as follows

    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()));
        }
    }
    

    And using it by:

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

    It works well and allows you to test your json strings without having to contact the real server!

    0 讨论(0)
  • 2020-12-07 07:35

    Adding to the answer by @Alec, I have extended the mock client to get the response directly from a text file in asset folder depending on the request URL.

    Ex

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

    Here the mock client, understands that the URL being fired is activate and looks for a file named activate.txt in the assets folder. It reads the content from assets/activate.txt file and sends it as response for the API.

    Here is the extended 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()));
        }
    }
    

    For a detailed explanation you can checkout my blog
    http://www.cumulations.com/blogs/13/Mock-API-response-in-Retrofit-using-custom-clients

    0 讨论(0)
提交回复
热议问题