Модульное тестирование с помощью Android volley

Я хотел бы знать, как я могу создавать модульные тесты для фреймворка volley. Смоделируйте запросы, ответы, чтобы я мог создавать модульные тесты, которые не требуют работы веб-сервиса и доступа к сети.

Я гуглил, но вообще не нашел много информации о фреймворке.


person apinho    schedule 04.09.2013    source источник
comment
В настоящее время нет ни одного документа, который я мог бы найти. Но есть много примеров, и видео Google IO 2013 также легко копается.   -  person Nevin Chen    schedule 29.10.2013
comment
Для других, которые приземляются здесь, рассмотрите возможность использования OkHttp вместо Volley. Это более современная среда, которую гораздо проще протестировать с помощью MockWebServer от OkHttp.   -  person sholsapp    schedule 12.12.2018


Ответы (4)


Я реализовал подкласс HttpStack с именем FakeHttpStack, который загружает поддельное тело ответа из локального файла, расположенного в res/raw. Я сделал это в целях разработки, то есть я могу разработать что-то для нового API до того, как сервер будет готов, но вы можете узнать что-то (например, переопределить HttpStack#peformRequest и createEntity) отсюда.

/**
 * Fake {@link HttpStack} that returns the fake content using resource file in res/raw.
 */
class FakeHttpStack implements HttpStack {
    private static final String DEFAULT_STRING_RESPONSE = "Hello";
    private static final String DEFAULT_JSON_RESPONSE = " {\"a\":1,\"b\":2,\"c\":3}";
    private static final String URL_PREFIX = "http://example.com/";
    private static final String LOGGER_TAG = "STACK_OVER_FLOW";

    private static final int SIMULATED_DELAY_MS = 500;
    private final Context context;

    FakeHttpStack(Context context) {
        this.context = context;
    }

    @Override
    public HttpResponse performRequest(Request<?> request, Map<String, String> stringStringMap)
            throws IOException, AuthFailureError {
        try {
            Thread.sleep(SIMULATED_DELAY_MS);
        } catch (InterruptedException e) {
        }
        HttpResponse response
                = new BasicHttpResponse(new BasicStatusLine(HttpVersion.HTTP_1_1, 200, "OK"));
        List<Header> headers = defaultHeaders();
        response.setHeaders(headers.toArray(new Header[0]));
        response.setLocale(Locale.JAPAN);
        response.setEntity(createEntity(request));
        return response;
    }

    private List<Header> defaultHeaders() {
        DateFormat dateFormat = new SimpleDateFormat("EEE, dd mmm yyyy HH:mm:ss zzz");
        return Lists.<Header>newArrayList(
                new BasicHeader("Date", dateFormat.format(new Date())),
                new BasicHeader("Server",
                        /* Data below is header info of my server */
                        "Apache/1.3.42 (Unix) mod_ssl/2.8.31 OpenSSL/0.9.8e")
        );
    }

    /**
     * returns the fake content using resource file in res/raw. fake_res_foo.txt is used for
     * request to http://example.com/foo
     */
    private HttpEntity createEntity(Request request) throws UnsupportedEncodingException {
        String resourceName = constructFakeResponseFileName(request);
        int resourceId = context.getResources().getIdentifier(
                resourceName, "raw", context.getApplicationContext().getPackageName());
        if (resourceId == 0) {
            Log.w(LOGGER_TAG, "No fake file named " + resourceName
                    + " found. default fake response should be used.");
        } else {
            InputStream stream = context.getResources().openRawResource(resourceId);
            try {
                String string = CharStreams.toString(new InputStreamReader(stream, Charsets.UTF_8));
                return new StringEntity(string);
            } catch (IOException e) {
                Log.e(LOGGER_TAG, "error reading " + resourceName, e);
            }
        }

        // Return default value since no fake file exists for given URL.
        if (request instanceof StringRequest) {
            return new StringEntity(DEFAULT_STRING_RESPONSE);
        }
        return new StringEntity(DEFAULT_JSON_RESPONSE);
    }

    /**
     * Map request URL to fake file name
     */
    private String constructFakeResponseFileName(Request request) {
        String reqUrl = request.getUrl();
        String apiName = reqUrl.substring(URL_PREFIX.length());
        return "fake_res_" + apiName;
    }
}

Чтобы использовать FakeHttpStack, вам просто нужно передать его в свою RequestQueue. Я также переопределяю RequestQueue.

public class FakeRequestQueue extends RequestQueue {
    public FakeRequestQueue(Context context) {
        super(new NoCache(), new BasicNetwork(new FakeHttpStack(context)));
    }
}

Хорошим моментом для этого подхода является то, что он не требует больших изменений в вашем коде. Вам просто нужно переключить RequestQueue на FakeRequestQueue при тестировании. Таким образом, его можно использовать при приемочном тестировании или тестировании системы.

С другой стороны, для модульного тестирования может быть более компактный способ. Например, вы можете реализовать свой подкласс Request.Listener как отдельный класс, чтобы можно было легко протестировать метод onResponse. Я рекомендую вам указать более подробно о том, что вы хотите протестировать, или добавить фрагмент кода.

person Kazuki    schedule 30.11.2013
comment
Вы можете расширить Volley.java и переопределить newRequestQueue(), чтобы получить FakeRequestQueue. Если вы используете внедрение зависимостей, вы можете просто внедрить свой MyVolley.java вместо Volley.java по умолчанию, чтобы код вашего приложения не требовал каких-либо изменений для тестирования. - person Christine; 25.09.2014
comment
Можете ли вы подсказать мне, как имитировать ответ на запрос залпа модульного тестирования. - person Dory; 16.09.2015
comment
Здравствуйте, я реализовал fakeRequestQueue, но он не дает никакого ответа. Посмотрите здесь stackoverflow.com/questions/32623129/ - person Dory; 17.09.2015
comment
Отличное решение. Я не переопределял RequestQueue, но у меня он все еще отлично работает. Большое спасибо. - person Mike Young; 31.01.2016

Взгляните на тесты папку, там можно найти примеры.

MockCache.java
MockHttpClient.java
MockHttpStack.java
MockHttpURLConnection.java
MockNetwork.java
MockRequest.java
MockResponseDelivery.java
person Dmytro Danylyk    schedule 22.11.2013

Не уверен на 100%, что я понимаю, что вы хотите сделать, но если я это сделаю, то easymock (библиотека, позволяющая создавать фиктивные классы, к которым вы можете обращаться и получать заранее определенные ответы) вам очень поможет. У парня по имени Ларс Фогель есть хорошая статья на эту тему, которую я нашел полезной некоторое время назад, когда использовал ее.

http://www.vogella.com/articles/EasyMock/article.html

person Carl-Emil Kjellstrand    schedule 07.09.2013

Вот копия MockHttpStack текущего залпа, упомянутый @Dmytro

package com.android.volley.mock;
import com.android.volley.AuthFailureError;
import com.android.volley.Request;
import com.android.volley.toolbox.HttpStack;
import org.apache.http.HttpResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class MockHttpStack implements HttpStack {
    private HttpResponse mResponseToReturn;
    private IOException mExceptionToThrow;
    private String mLastUrl;
    private Map<String, String> mLastHeaders;
    private byte[] mLastPostBody;
    public String getLastUrl() {
        return mLastUrl;
    }
    public Map<String, String> getLastHeaders() {
        return mLastHeaders;
    }
    public byte[] getLastPostBody() {
        return mLastPostBody;
    }
    public void setResponseToReturn(HttpResponse response) {
        mResponseToReturn = response;
    }
    public void setExceptionToThrow(IOException exception) {
        mExceptionToThrow = exception;
    }
    @Override
    public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
            throws IOException, AuthFailureError {
        if (mExceptionToThrow != null) {
            throw mExceptionToThrow;
        }
        mLastUrl = request.getUrl();
        mLastHeaders = new HashMap<String, String>();
        if (request.getHeaders() != null) {
            mLastHeaders.putAll(request.getHeaders());
        }
        if (additionalHeaders != null) {
            mLastHeaders.putAll(additionalHeaders);
        }
        try {
            mLastPostBody = request.getBody();
        } catch (AuthFailureError e) {
            mLastPostBody = null;
        }
        return mResponseToReturn;
    }
}
person serv-inc    schedule 05.06.2018