Android Retrofit 单元测试 Unit Test

2,001 阅读1分钟

Model

后端返回的结果都比较统一,一般包含 codemsgdata,创建的 model 使用泛型。

public class Result<T> {
    private int code;
    private String msg;
    private T data;
    // getter & setter
}

subModel

public class SubModel {
    private long state;
    private long remain;
    private long total;
    // getter & setter
}

Retrofit 网络库

官方网站:square.github.io/retrofit/

配置 app build.gradle

dependencies {
    ...
    implementation 'com.squareup.retrofit2:retrofit:2.6.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.6.0'
    implementation("com.squareup.okhttp3:logging-interceptor:3.14.2")
}

Server

public interface Service {
    @FormUrlEncoded
    @POST("ctrl")
    Call<Result> sendCmd(@Field("cmd") String cmd, @Field("arg") String arg);

    @POST("config")
    Call<Result<Config>> fetchConfig();

    @POST("state")
    Call<Result<State>> fetchState();

    @POST("tfcard")
    Call<Result<TFCard>> fetchTFCard();
}

Injection 注入

public class Injection {

    // 创建 SingleObject 的一个对象
    private static Injection instance = new Injection();

    // 让构造函数为 private,这样该类就不会被实例化
    private Injection(){}

    // 获取唯一可用的对象
    public static Injection getInstance(){
        return instance;
    }

    private Retrofit provideRetrofit() {
        final String BASE_URL = "http://127.0.0.1:5000/api/";
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .client(provideOkHttpClient())
                .build();
        return retrofit;
    }

    private OkHttpClient provideOkHttpClient() {
        OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
        httpClient.addInterceptor(provideLoggingInterceptor());
        return httpClient.build();
    }

    private HttpLoggingInterceptor provideLoggingInterceptor() {
        HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
        if (BuildConfig.DEBUG) {
            logging.setLevel(HttpLoggingInterceptor.Level.BODY);
        } else {
            logging.setLevel(HttpLoggingInterceptor.Level.NONE);
        }
        return logging;
    }

    public Service provideService() {
        return provideRetrofit().create(Service.class);
    }
}

Unit Test 单元测试

async 异步测试

public void testSomething() {
    final CountDownLatch signal = new CountDownLatch(1);
    Service.doSomething(new Callback() {

        @Override
        public void onResponse() {
            // test response data
            // assertEquals(..
            // assertTrue(..
            // etc
            signal.countDown(); // notify the count down latch
        }

    });
    signal.await(); // wait for callback
}

java.lang.RuntimeException:

java.lang.RuntimeException: Method d in android.util.Log not mocked. See http://g.co/androidstudio/not-mocked for details.

build.gradle 下面 android 项目下添加:

android {
    ...
   testOptions { 
       unitTests.returnDefaultValues = true
   }
}

添加测试用例

public class InjectionTest {
    @Test
    public void provideRecoderServiceTFCard() throws Throwable {
        final CountDownLatch signal = new CountDownLatch(1);
        Call<Result<TFCard>> call = Injection.getInstance().provideRecoderService().fetchTFCard();

        call.enqueue(new Callback<Result<TFCard>>() {
            @Override
            public void onResponse(Call<Result<TFCard>> call, Response<Result<TFCard>> response) {
                Result result = response.body();
                TFCard tfCard = (TFCard) result.getData();
                LogUtils.d("tfCard", tfCard.toString());
                signal.countDown();
            }

            @Override
            public void onFailure(Call<Result<TFCard>> call, Throwable t) {
                LogUtils.d("InjectionTest", t.getMessage());
                signal.countDown();
            }
        });

        signal.await(3, TimeUnit.SECONDS);
    }
}