Квадратный модифицированный сервер макет для тестирования
Каков наилучший способ издеваться над сервером для тестирования при использовании квадратной рамочной структуры.
Потенциальные пути:
-
Создайте новую модификацию client и установите ее в RestAdapter.Builder(). setClient(). Это включает в себя анализ объекта Request и возвращение json в качестве объекта Response.
-
Внедрите этот аннотированный интерфейс в качестве класса mock и используйте это вместо версии, предоставленной RestAdapter.create() (не тестируйте сериализацию gson)
-
В идеале я хочу, чтобы moker-сервер предоставлял json-ответы, чтобы я мог одновременно тестировать сериализацию gson.
Приветствуются любые примеры.
Ответы
Ответ 1
Я решил попробовать метод 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());
Он хорошо работает и позволяет вам тестировать ваши json-строки без необходимости связываться с реальным сервером!
Ответ 2
Mock Retrofit 2.0 Запросы на тестирование
Поскольку старые механизмы, такие как создание класса MockClient
и реализация его из Client
, больше не работают с Retrofit 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().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;
}
}
Исходный код проекта на GitHub
Ответ 3
Тестирование десериализации JSON на ваши объекты (предположительно с помощью TypeAdapters
?) кажется отдельной проблемой, требующей отдельных модульных тестов.
Я использую версию 2 лично. Он предоставляет безопасный для использования с текстом код, который легко отлаживается и изменяется. В конце концов, что хорошего объявляет ваш API как интерфейсы, если вы не создаете альтернативные версии для тестирования! Полиморфизм для победы.
Другой вариант - использование Java Proxy
. Это на самом деле то, как Retrofit (в настоящее время) реализует свое базовое HTTP-взаимодействие. Это, по общему признанию, потребует больше работы, но позволит гораздо более динамичные макеты.
Ответ 4
Вы также можете использовать что-то вроде Webservermock из Squareup! → https://github.com/square/okhttp/tree/master/mockwebserver
Ответ 5
Я большой поклонник Apiary.io для насмешки API, прежде чем переходить на реальный сервер.
Вы также можете использовать плоские файлы .json и читать их из файловой системы.
Вы также можете использовать общедоступный API, такой как Twitter, Flickr и т.д.
Вот некоторые другие полезные ресурсы о Retrofit.
Слайды: https://docs.google.com/presentation/d/12Eb8OPI0PDisCjWne9-0qlXvp_-R4HmqVCjigOIgwfY/edit#slide=id.p
Видео: http://www.youtube.com/watch?v=UtM06W51pPw&feature=g-user-u
Пример проекта: https://github.com/dustin-graham/ucad_twitter_retrofit_sample
Ответ 6
Mockery (отказ от ответственности: Im автор) был разработан именно для этой цели.
Mockery - это издевательская/тестовая библиотека, ориентированная на проверку сетевых уровней со встроенной поддержкой Retrofit. Он автоматически генерирует тесты JUnit, основанные на спецификациях данного Api. Идея состоит в том, чтобы не писать вручную никаких тестов; ни внедрение интерфейсов для издевательства ответов сервера.
Ответ 7
-
Сначала создайте свой интерфейс Retrofit.
public interface LifeKitServerService {
/**
* query event list from server,convert Retrofit Call to RxJava 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);
}
}
-
Если вы используете второй вариант (используйте интерфейс Retrofit для данных Mock-сервера), вам нужно 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!!!
});
}
Спасибо за чтение.
Ответ 8
Добавляя к ответу @Alec, я расширил mock-клиент, чтобы получить ответ непосредственно из текстового файла в папке с активами в зависимости от 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
Ответ 9
JSONPlaceholder: Fake Online REST API для тестирования и создания прототипов
https://jsonplaceholder.typicode.com/
ReqresIn: еще один онлайн REST API
https://reqres.in/
Почтальон макет сервера
Если вы хотите протестировать пользовательскую полезную нагрузку ответа, приведенные выше два могут не соответствовать вашим требованиям, тогда вы можете попробовать почтовый макет сервера. Это довольно легко настроить и гибко определить свой собственный запрос и полезную нагрузку ответа.
https://learning.getpostman.com/docs/postman/mock_servers/intro_to_mock_servers/ https://youtu.be/shYn3Ys3ygE
Ответ 10
Для меня пользовательский Retrofit Client отличен благодаря гибкости. Особенно, когда вы используете любой каркас DI, вы можете быстро и просто включать/выключать макет. Я использую пользовательский клиент, предоставленный Dagger, также в модульных и интеграционных тестах.
Изменить:
Здесь вы найдете пример издевательской модернизации
https://github.com/pawelByszewski/retrofitmock
Ответ 11
Пересмешивать вызовы API с помощью Retrofit теперь еще проще с Mockinizer, что делает работу с MockWebServer очень простой:
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)
}
)
Просто создайте карту RequestFilter и MockResponses, а затем вставьте ее в цепочку компоновщика OkHttpClient:
OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.mockinize(mocks) // <-- just plug in your custom mocks here
.build()
Вам не нужно беспокоиться о настройке MockWebServer и т.д. Просто добавьте свои макеты, все остальное сделает для вас Mockinizer.
(Отказ от ответственности: я автор Mockinizer)