Mô hình máy chủ trang bị thêm hình vuông để thử nghiệm


97

Cách tốt nhất để giả lập một máy chủ để thử nghiệm khi sử dụng khung trang bị thêm hình vuông là gì .

Những cách tiềm năng:

  1. Tạo một ứng dụng khách trang bị thêm mới và đặt nó trong RestAdapter.Builder (). SetClient (). Điều này liên quan đến việc phân tích cú pháp đối tượng Yêu cầu và trả về json dưới dạng đối tượng Phản hồi.

  2. Triển khai giao diện được chú thích này dưới dạng một lớp giả và sử dụng giao diện đó thay cho phiên bản được cung cấp bởi RestAdapter.create () (sẽ không kiểm tra tuần tự hóa gson)

  3. ?

Lý tưởng nhất là tôi muốn máy chủ giả mạo cung cấp phản hồi json để tôi có thể kiểm tra tuần tự hóa gson cùng một lúc.

Bất kỳ ví dụ sẽ được đánh giá rất cao.


@JakeWharton, mục đích là square-ossgì? Nó có vẻ thừa được đưa ra retrofit.
Charles

@Alec Holmes: Bạn đã giải quyết được vấn đề của mình chưa?
AndiGeeky 30/12/16

Câu trả lời:


104

Yêu cầu Mock Retrofit 2.0 để thử nghiệm

Vì các cơ chế cũ như tạo MockClientlớp và triển khai nó từ Clientđó không hoạt động nữa với Retrofit 2.0, ở đây tôi mô tả một cách mới để thực hiện điều đó. Tất cả những gì bạn cần làm bây giờ là thêm các bộ đánh chặn tùy chỉnh của bạn cho OkHttpClient như hình minh họa bên dưới . FakeInterceptorlớp chỉ ghi đè interceptphương thức và trong trường hợp nếu ứng dụng ở DEBUGchế độ trả về JSON đã cho.

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

Mã nguồn của dự án trên GitHub


9
Để tránh UnsupportedOperationException, hãy sử dụng OkHttpClient.Builder. cuối cùng OkHttpClient okHttpClient = new OkHttpClient.Builder () .addInterceptor (new FakeInterceptor ()) .build ();
John

4
Hai vấn đề tôi gặp phải: 1- Không có uri()dưới chain.request().uri()(Tôi đã sửa lỗi này String url = chain.request().url().toString();tùy trường hợp của tôi khác). 2- Tôi nhận được java.lang.IllegalStateException: network interceptor my.package.name.FakeInterceptor must call proceed() exactly once. Tôi đã thêm điều này vào addNetworkInterceptor()thay vì addInterceptor().
Hesam

2
sử dụng chain.request (). url (). uri ();
Amol Gupta

Làm cách nào tôi có thể mô phỏng lỗi 401 để kiểm tra phương thức httpClient.authenticator? bằng cách đặt mã "401" phương thức xác thực không gọi. làm thế nào tôi có thể xử lý điều này?
Mahdi

Tôi đã thực hiện phương pháp đánh chặn giả mạo để chế nhạo các ứng dụng web lên cấp độ tiếp theo và xuất bản một thư viện nhỏ để làm cho nó dễ dàng và thuận tiện hơn. Xem github.com/donfuxx/Mockinizer
donfuxx

85

Tôi quyết định thử phương pháp 1 như sau

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

Và sử dụng nó bằng cách:

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

Nó hoạt động tốt và cho phép bạn kiểm tra các chuỗi json của mình mà không cần phải liên hệ với máy chủ thực!


Tôi đã cập nhật phương thức khởi tạo Đáp ứng được sử dụng vì phương thức cũ không được dùng nữa, điều này đang ném vào IllegalArgumentException url == nullvới Retrofit 1.4.1.
Dan J

1
Cũng cần thêm một thiết bị đầu cuối cho người xây dựng:builder.setEndpoint("http://mockserver.com").setClient(new MockClient());
codeprogression

Tôi đã mở rộng ứng dụng khách giả ở trên để tìm nạp phản hồi từ tệp trong thư mục nội dung tùy thuộc vào yêu cầu URL.
praveena_kd

21
Retrofit 2 hiện sử dụng OkHttpClient cho lớp máy khách và mã này không hoạt động. ¿Có bất kỳ ý tưởng nào về cách tạo mô hình OkHttpClient không? Có lẽ đó là tất cả về việc mở rộng và ghi đè nó, nhưng tôi không chắc làm thế nào.
GuillermoMP

1
Bạn có thể cập nhật câu trả lời của mình dựa trên Retrofit2 không? cảm ơn
Hesam

20

Kiểm tra giải mã JSON đối với các đối tượng của bạn (có lẽ là với TypeAdapters?) Có vẻ như là một vấn đề riêng biệt yêu cầu các bài kiểm tra đơn vị riêng biệt.

Cá nhân tôi sử dụng phiên bản 2. Nó cung cấp mã loại an toàn, thân thiện với cấu trúc lại, có thể dễ dàng gỡ lỗi và thay đổi. Rốt cuộc, điều tốt là khai báo API của bạn là các giao diện nếu bạn không tạo các phiên bản thay thế của chúng để thử nghiệm! Đa hình cho phần thắng.

Một tùy chọn khác là sử dụng Java Proxy. Đây thực sự là cách Retrofit (hiện tại) triển khai tương tác HTTP cơ bản của nó. Điều này được thừa nhận sẽ đòi hỏi nhiều công việc hơn, nhưng sẽ cho phép tạo ra nhiều chế độ năng động hơn.


Đây cũng là cách ưa thích của tôi. Nó đơn giản hơn rất nhiều để gỡ lỗi như đã nêu ở trên, sau đó phải xử lý trực tiếp với cơ quan phản hồi. @alec Nếu bạn muốn kiểm tra tuần tự hóa GSON, hãy tạo / đọc trong một chuỗi json và sử dụng một đối tượng gson để giải mã trên không. Tôi tin rằng đó là những gì Retrofit làm.
loeschg

@JakeWharton Bạn có thể cung cấp một ví dụ ngắn gọn về điều này muốn được không? Tôi gặp khó khăn khi hình dung điều này ... Cảm ơn!
Uncle_tex



8

Tôi là một fan hâm mộ lớn của Apiary.io vì đã chế nhạo một API trước khi chuyển sang một máy chủ thực.

Bạn cũng có thể sử dụng các tệp .json phẳng và đọc chúng từ hệ thống tệp.

Bạn cũng có thể sử dụng API có thể truy cập công khai như Twitter, Flickr, v.v.

Dưới đây là một số tài nguyên tuyệt vời khác về Trang bị thêm.

Trang trình bày: 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

Dự án mẫu: https://github.com/dustin-graham/ucad_twitter_retrofit_sample


7

Mockery (từ chối trách nhiệm: Tôi là tác giả) được thiết kế chỉ cho nhiệm vụ chính xác này.

Mockery là một thư viện chế nhạo / thử nghiệm tập trung vào việc xác thực các lớp mạng với hỗ trợ tích hợp cho Retrofit. Nó tự động tạo các bài kiểm tra JUnit dựa trên các thông số kỹ thuật của Api nhất định. Ý tưởng là không cần phải viết thủ công bất kỳ bài kiểm tra nào; không triển khai các giao diện để chế nhạo phản hồi của máy chủ.


7
  1. Đầu tiên, tạo giao diện Retrofit của bạn.

    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. Người yêu cầu của bạn theo sau:

    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. Nếu bạn sử dụng sự lựa chọn thứ hai (sử dụng giao diện Retrofit cho dữ liệu máy chủ Mock), bạn cần MockRetrofit, sử dụng mã sau:

    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. Dữ liệu của tôi từ tệp tài sản (Asset / server / EventList.json), nội dung tệp này là:

    {
      "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.Nếu bạn đang sử dụng bộ đánh chặn okhttp3, bạn cần phải tự xác định bộ đánh chặn, như sau:

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.Cuối cùng, bạn có thể yêu cầu máy chủ của mình với mã:

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

Cảm ơn vì đã đọc.


5

Thêm vào câu trả lời của @Alec, tôi đã mở rộng ứng dụng khách giả để nhận phản hồi trực tiếp từ tệp văn bản trong thư mục nội dung tùy thuộc vào URL yêu cầu.

Ví dụ

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

Ở đây, ứng dụng giả hiểu rằng URL đang được kích hoạt được kích hoạt và tìm kiếm tệp có tên là active.txt trong thư mục nội dung. Nó đọc nội dung từ tệp tài sản / active.txt và gửi nó dưới dạng phản hồi cho API.

Đây là phần mở rộng 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()));
    }
}

Để được giải thích chi tiết, bạn có thể xem blog của tôi
http://www.cumulation.com/blogs/13/Mock-API-response-in-Retrofit-using-custom-clients


xin chào, Khi tôi đang viết một lớp thử nghiệm bằng cách sử dụng rô-bốt và sử dụng ứng dụng giả để mô phỏng api trang bị thêm, nó không cho tôi bất kỳ phản hồi nào. Bạn có thể hướng dẫn tôi làm thế nào để làm điều này.
Dory

Xin chào @Dory, hãy đảm bảo rằng bạn có phần kết thúc URL và tên tệp bên trong thư mục nội dung. Ví dụ: giả sử URL của bạn như bên dưới (sử dụng Reftrofit tại đây) @POST ("/ RedGyft") public void RedGyft (@Body MposRequest reqdata, Callback <RedeemGyftResponse> callback); sau đó tên tập tin trong thư mục correspodning tài sản là redeemgyft.txt
praveena_kd

Tôi đã đặt tên tệp tĩnh, trong MockClienttệp của tôi , đã viết một lớp thử nghiệm bằng cách sử dụng rô-bốt. Nhưng tôi không thể nhận được bất kỳ phản hồi nào từ tệp json.
Dory

nếu bạn đã giữ tệp bên trong thư mục nội dung, nó sẽ chọn nó.
praveena_kd

1

JSONPlaceholder: API REST trực tuyến giả mạo để thử nghiệm và tạo mẫu

https://jsonplaceholder.typicode.com/

ReqresIn: Một API REST trực tuyến khác

https://reqres.in/

Máy chủ giả của người đưa thư

Nếu bạn muốn kiểm tra tải trọng phản hồi tùy chỉnh, hai điều trên có thể không phù hợp với yêu cầu của bạn, thì bạn có thể thử máy chủ giả của người đưa thư. Nó khá dễ dàng để thiết lập và linh hoạt để xác định tải trọng yêu cầu và phản hồi của riêng bạn.

nhập mô tả hình ảnh ở đây https://learning.getpostman.com/docs/postman/mock_servers/intro_to_mock_servers/ https://youtu.be/shYn3Ys3ygE


1

Việc chế nhạo các cuộc gọi api với Retrofit giờ đây thậm chí còn dễ dàng hơn với Mockinizer , giúp làm việc với MockWebServer thực sự dễ dàng:

import com.appham.mockinizer.RequestFilter
import okhttp3.mockwebserver.MockResponse

val mocks: Map<RequestFilter, MockResponse> = mapOf(

    RequestFilter("/mocked") to MockResponse().apply {
        setResponseCode(200)
        setBody("""{"title": "Banana Mock"}""")
    },

    RequestFilter("/mockedError") to MockResponse().apply {
        setResponseCode(400)
    }

)

Chỉ cần tạo một bản đồ của RequestFilter và MockResponses , sau đó cắm nó vào chuỗi trình tạo OkHttpClient của bạn:

OkHttpClient.Builder()
            .addInterceptor(loggingInterceptor)
            .mockinize(mocks) // <-- just plug in your custom mocks here
            .build()

Bạn không phải lo lắng về việc định cấu hình MockWebServer, v.v. Chỉ cần thêm mô hình của bạn, tất cả những việc còn lại sẽ được Mockinizer thực hiện cho bạn.

(Tuyên bố từ chối trách nhiệm: Tôi là tác giả của Mockinizer)


0

Đối với tôi, Khách hàng trang bị thêm tùy chỉnh là rất tốt vì tính linh hoạt. Đặc biệt khi bạn sử dụng bất kỳ khung DI nào, bạn có thể bật / tắt mô hình nhanh chóng và đơn giản. Tôi đang sử dụng Ứng dụng khách tùy chỉnh do Dagger cung cấp trong các thử nghiệm đơn vị và tích hợp.

Chỉnh sửa: Tại đây, bạn tìm thấy ví dụ về trang bị bổ sung chế nhạo https://github.com/pawelByszewski/retrofitmock

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.