如何在使用 Retrofit 2 进行单元测试期间创建 retrofit.Response 对象
How to create a retrofit.Response object during Unit Testing with Retrofit 2
在使用 RxJava 和 Retrofit 2 时,我正在尝试创建单元测试来涵盖我的应用何时收到特定响应。
我遇到的问题是,在 Retrofit 2 中,我无法找到不使用反射创建 retrofit.Response 对象的好方法。
@Test
public void testLogin_throwsLoginBadRequestExceptionWhen403Error() {
Request.Builder requestBuilder = new Request.Builder();
requestBuilder.get();
requestBuilder.url("http://localhost");
Response.Builder responseBuilder = new Response.Builder();
responseBuilder.code(403);
responseBuilder.protocol(Protocol.HTTP_1_1);
responseBuilder.body(ResponseBody.create(MediaType.parse("application/json"), "{\"key\":[\"somestuff\"]}"));
responseBuilder.request(requestBuilder.build());
retrofit.Response<LoginResponse> aResponse = null;
try {
Constructor<retrofit.Response> constructor= (Constructor<retrofit.Response>) retrofit.Response.class.getDeclaredConstructors()[0];
constructor.setAccessible(true);
aResponse = constructor.newInstance(responseBuilder.build(), null, null);
} catch (Exception ex) {
//reflection error
}
doReturn(Observable.just(aResponse)).when(mockLoginAPI).login(anyObject());
TestSubscriber testSubscriber = new TestSubscriber();
loginAPIService.login(loginRequest).subscribe(testSubscriber);
Throwable resultError = (Throwable) testSubscriber.getOnErrorEvents().get(0);
assertTrue(resultError instanceof LoginBadRequestException);
}
我尝试使用以下方法,但这会创建 OkHttp 响应与改造响应。
Request.Builder requestBuilder = new Request.Builder();
requestBuilder.get();
requestBuilder.url("http://localhost");
Response.Builder responseBuilder = new Response.Builder();
responseBuilder.code(403);
responseBuilder.protocol(Protocol.HTTP_1_1);
retrofit.Response
class有创建实例的静态工厂方法:
public static <T> Response<T> success(T body) {
/* ... */
}
public static <T> Response<T> success(T body, com.squareup.okhttp.Response rawResponse) {
/* ... */
}
public static <T> Response<T> error(int code, ResponseBody body) {
/* ... */
}
public static <T> Response<T> error(ResponseBody body, com.squareup.okhttp.Response rawResponse) {
/* ... */
}
例如:
Account account = ...;
retrofit.Response<Account> aResponse = retrofit.Response.success(account);
或:
retrofit.Response<Account> aResponse = retrofit.Response.error(
403,
ResponseBody.create(
MediaType.parse("application/json"),
"{\"key\":[\"somestuff\"]}"
)
);
注意:在最新的Kotlin Retrofit版本(2.7.1)中,推荐使用这样的扩展方式:
Response.error(
400,
"{\"key\":[\"somestuff\"]}"
.toResponseBody("application/json".toMediaTypeOrNull())
)
这属于 有效 Java 项 1:考虑静态工厂方法而不是构造函数。
以下是仅模拟改造响应的方法
首先你需要在build.gradle中添加这些依赖:
// mock websever for testing retrofit responses
testImplementation "com.squareup.okhttp3:mockwebserver:4.6.0"
testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0"
模拟成功的 200 响应:
val mockResponseBody = Mockito.mock(MoviesResponse::class.java)
val mockResponse = Response.success(mockResponseBody)
模拟不成功的响应(例如 400、401、404):
val errorResponse =
"{\n" +
" \"type\": \"error\",\n" +
" \"message\": \"What you were looking for isn't here.\"\n"
+ "}"
val errorResponseBody = errorResponse.toResponseBody("application/json".toMediaTypeOrNull())
val mockResponse = Response.error<String>(400, errorResponseBody)
无需创建模拟网络服务器和所有额外工作。
使用 Response.Builder
的 Kotlin + Mockito + okhttp3 示例
val mockResponse: Response<MyResponseReturnType> =
Response.success(mock<MyResponseReturnType>(),
okhttp3.Response.Builder()
.code(200)
.message("Response.success()")
.protocol(Protocol.HTTP_1_1)
.request(Request.Builder().url("http://test-url/").build())
.receivedResponseAtMillis(1619053449513)
.sentRequestAtMillis(1619053443814)
.build())
我没有在字符串上使用 toResponseBody()
扩展函数,而是在字节数组上使用它,如下所示。
Response.error(400, byteArrayOf().toResponseBody())
byteArrayOf().toResponseBody()
会创建一个 ResponseBody。
在使用 RxJava 和 Retrofit 2 时,我正在尝试创建单元测试来涵盖我的应用何时收到特定响应。
我遇到的问题是,在 Retrofit 2 中,我无法找到不使用反射创建 retrofit.Response 对象的好方法。
@Test
public void testLogin_throwsLoginBadRequestExceptionWhen403Error() {
Request.Builder requestBuilder = new Request.Builder();
requestBuilder.get();
requestBuilder.url("http://localhost");
Response.Builder responseBuilder = new Response.Builder();
responseBuilder.code(403);
responseBuilder.protocol(Protocol.HTTP_1_1);
responseBuilder.body(ResponseBody.create(MediaType.parse("application/json"), "{\"key\":[\"somestuff\"]}"));
responseBuilder.request(requestBuilder.build());
retrofit.Response<LoginResponse> aResponse = null;
try {
Constructor<retrofit.Response> constructor= (Constructor<retrofit.Response>) retrofit.Response.class.getDeclaredConstructors()[0];
constructor.setAccessible(true);
aResponse = constructor.newInstance(responseBuilder.build(), null, null);
} catch (Exception ex) {
//reflection error
}
doReturn(Observable.just(aResponse)).when(mockLoginAPI).login(anyObject());
TestSubscriber testSubscriber = new TestSubscriber();
loginAPIService.login(loginRequest).subscribe(testSubscriber);
Throwable resultError = (Throwable) testSubscriber.getOnErrorEvents().get(0);
assertTrue(resultError instanceof LoginBadRequestException);
}
我尝试使用以下方法,但这会创建 OkHttp 响应与改造响应。
Request.Builder requestBuilder = new Request.Builder();
requestBuilder.get();
requestBuilder.url("http://localhost");
Response.Builder responseBuilder = new Response.Builder();
responseBuilder.code(403);
responseBuilder.protocol(Protocol.HTTP_1_1);
retrofit.Response
class有创建实例的静态工厂方法:
public static <T> Response<T> success(T body) {
/* ... */
}
public static <T> Response<T> success(T body, com.squareup.okhttp.Response rawResponse) {
/* ... */
}
public static <T> Response<T> error(int code, ResponseBody body) {
/* ... */
}
public static <T> Response<T> error(ResponseBody body, com.squareup.okhttp.Response rawResponse) {
/* ... */
}
例如:
Account account = ...;
retrofit.Response<Account> aResponse = retrofit.Response.success(account);
或:
retrofit.Response<Account> aResponse = retrofit.Response.error(
403,
ResponseBody.create(
MediaType.parse("application/json"),
"{\"key\":[\"somestuff\"]}"
)
);
注意:在最新的Kotlin Retrofit版本(2.7.1)中,推荐使用这样的扩展方式:
Response.error(
400,
"{\"key\":[\"somestuff\"]}"
.toResponseBody("application/json".toMediaTypeOrNull())
)
这属于 有效 Java 项 1:考虑静态工厂方法而不是构造函数。
以下是仅模拟改造响应的方法
首先你需要在build.gradle中添加这些依赖:
// mock websever for testing retrofit responses
testImplementation "com.squareup.okhttp3:mockwebserver:4.6.0"
testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0"
模拟成功的 200 响应:
val mockResponseBody = Mockito.mock(MoviesResponse::class.java)
val mockResponse = Response.success(mockResponseBody)
模拟不成功的响应(例如 400、401、404):
val errorResponse =
"{\n" +
" \"type\": \"error\",\n" +
" \"message\": \"What you were looking for isn't here.\"\n"
+ "}"
val errorResponseBody = errorResponse.toResponseBody("application/json".toMediaTypeOrNull())
val mockResponse = Response.error<String>(400, errorResponseBody)
无需创建模拟网络服务器和所有额外工作。
使用 Response.Builder
的 Kotlin + Mockito + okhttp3 示例val mockResponse: Response<MyResponseReturnType> =
Response.success(mock<MyResponseReturnType>(),
okhttp3.Response.Builder()
.code(200)
.message("Response.success()")
.protocol(Protocol.HTTP_1_1)
.request(Request.Builder().url("http://test-url/").build())
.receivedResponseAtMillis(1619053449513)
.sentRequestAtMillis(1619053443814)
.build())
我没有在字符串上使用 toResponseBody()
扩展函数,而是在字节数组上使用它,如下所示。
Response.error(400, byteArrayOf().toResponseBody())
byteArrayOf().toResponseBody()
会创建一个 ResponseBody。