gpt4 book ai didi

android - 使用 retrofit2 和 rxjava2 对 android 应用程序进行单元测试

转载 作者:行者123 更新时间:2023-11-29 23:22:22 25 4
gpt4 key购买 nike

注意:我使用 () 而不是尖括号 <> 作为数据类型,因为找不到让它们在论坛上工作的方法。

我有一个 MVP android 应用程序,它使用 Retofit2 和 RxJava2 从 GitHub Api 获取数据。代码工作正常,我能够恢复 Observable(Response(List(Headers))),其中 Response 来自 Retrofit2,Headers 来自 OkHttp3。

但是当涉及到单元测试时,我遇到了一个问题:我无法模拟 Response(List(Headers))。Retrofit2 Response 类有一个私有(private)构造函数,所以我无法创建它的实例。然后我尝试将 OkHttp MockWebServer 与具有 MockResponse(List(Headers)) 的想法一起使用。尽管我可以将 header 设置为 MockResponse 实例,但我无法获得 MockResponse(List(Headers))

我的服务:

@GET("users/{username}/repos")
Observable<Response<List<Headers>>> checkReposPerUser(@Path("username") String owner,
@Query("access_token") String accessTokenString,
@Query("token_type") String accessTokenTypeString,
@Query("per_page") String perPageValue);

我的主持人:

    @Override
public void checkRepoPerUser(String owner) {

//recovering access token data from Shared Preferences;
String accessTokenString = repository.getAccessTokenString();
String accessTokenTypeString = repository.getAccessTokenType();

//Asking for a list of repositories with 1 repository per page.
//This let us know how many repositories we found and also to deal with error response code
Disposable disposable = repository.checkReposPerUser(owner, accessTokenString, accessTokenTypeString, "1")
.subscribeOn(ioScheduler)
.observeOn(uiScheduler)
.subscribe(this::handleReturnedHeaderData, this::handleHeaderError);
disposeBag.add(disposable);
}

@VisibleForTesting
public void handleReturnedHeaderData(Response<List<Headers>> response) {
//getting value 'Link' from response headers in order to count the repositories
String link = response.headers().get("Link");
String message = response.message();

//checking GitHub API requests limit
String limit = response.headers().get("X-RateLimit-Limit");
Log.d(TAG, "Limit requests: " + limit);
String limitRemaining = response.headers().get("X-RateLimit-Remaining");
Log.d(TAG, "Limit requests remaining: " + limitRemaining);

//getting http response code
int code = response.code();

switch (code){
case 404:
if(message.equalsIgnoreCase("not found")){ //User not exists
view.showUserNotFoundMessage();
}else{
view.showErrorMessage(message);
}
break;
case 403:
//GitHub API requests limit reached
//Instead of showing an error, we start the login process,
// store another access token in shared Preferences and resend the same request that failed before
view.startLogin();
break;
case 200:
if(link == null){ //Link value is not present into the header, it means there's 0 or 1 repo
Log.d(TAG, "Total repos for current user is 0 or 1.");
//get the repository
searchRepo(view.getOwner()); //Starting looking for data
}else if( link != null){
//get last page number: considering that we requested all the repos paginated with
//only 1 repo per page, the last page number is equal to the total number of repos
String totalRepoString = link.substring(link.lastIndexOf("&page=") + 6, link.lastIndexOf(">"));
Log.d(TAG, "Total repos for current user are " + totalRepoString);

// TODO once we know how many repositories we have, we can decide how many calls to do (total repositories/100 rounded up )

//get the repositories
searchRepo(view.getOwner()); //Starting 3 looking for data
}
break;
default:
searchRepo(view.getOwner()); //Starting 3 looking for data
break;
}
}

现在,我想对 handleReturnedHeaderData(Response(List(Headers)) response) 进行单元测试。这是测试类:

public class RepositoriesPresenterTest {
private static final Repo REPO1 = new Repo();
private static final Repo REPO2 = new Repo();
private static final Repo REPO3 = new Repo();
private static final List<Repo> NO_REPOS = Collections.emptyList();
private static final List<Repo> THREE_REPOS = Arrays.asList(REPO1, REPO2, REPO3);

public static final String OWNER = "owner";
public static final String ACCESS_TOKEN_STRING = "access_token_string";
public static final String ACCESS_TOKEN_TYPE = "access_token_type";
public static final String PER_PAGE_VALUE = "per_page_value";

@Parameterized.Parameters
public static Object[] data() {
return new Object[] {NO_REPOS, THREE_REPOS};
}

@Parameterized.Parameter
public List<Repo> repos;

@Rule
public MockitoRule mockitoRule = MockitoJUnit.rule();

@Mock private GitHubChallengeRepository repositoryMock;

@Mock private RepositoriesContract.View viewMock;

@Mock private Response<List<Headers>> responseListHeaders;


private TestScheduler testScheduler;

private RepositoriesPresenter SUT; //System Under Test

@Before public void setUp() {
testScheduler = new TestScheduler();
SUT = new RepositoriesPresenter(repositoryMock, viewMock, testScheduler, testScheduler);
}


}

我尝试进入 RepositoriesPresenterTest 类:

@Test public void repoPresenter_CheckRepoPerUser_responseHeadersListExpected() throws Exception {

// Mock a response with status 200 and some Headers
MockResponse mockResponse = new MockResponse()
.setResponseCode(200)
.setBody("{}")
.setHeader("Status", "status_value")
.setHeader("X-RateLimit-Limit", "60")
.setHeader("X-RateLimit-Remaining", "57");
List<Headers> headersList = Arrays.asList(mockResponse.getHeaders());

// Given
given(repositoryMock.getAccessTokenString()).willReturn(ACCESS_TOKEN_STRING);
given(repositoryMock.getAccessTokenType()).willReturn(ACCESS_TOKEN_TYPE);
given(repositoryMock.checkReposPerUser(
OWNER,
ACCESS_TOKEN_STRING,
ACCESS_TOKEN_TYPE,
PER_PAGE_VALUE)).willReturn((Observable<Response<List<Headers>>>) Observable.just(headersList));

// When
SUT.checkRepoPerUser(OWNER);
testScheduler.triggerActions();

// Then
then(viewMock).should().getOwner();

}

但它无法编译,因为我无法将 Observable.just(headersList) 转换为一个 Observable(Response(List(Headers)))

之后我尝试使用 MockWebServer 来模拟连接(即使我认为测试没有必要):

@Test
public void repoPresenter_CheckRepoPerUser_responseHeadersListExpected() throws InterruptedException, IOException {


MockWebServer mockWebServer = new MockWebServer();

TestObserver testObserver = new TestObserver<Response<List<Headers>>>();

String path = "\"users/{username}/repos\"";

// Mock a response with status 200 and some Headers
MockResponse mockResponse = new MockResponse()
.setResponseCode(200)
.setBody("{}")
.setHeader("Status", "status_value")
.setHeader("X-RateLimit-Limit", "60")
.setHeader("X-RateLimit-Remaining", "57");
// Enqueue request
mockWebServer.enqueue(mockResponse);

// Call the API
repositoryMock.checkReposPerUser(
OWNER,
ACCESS_TOKEN_STRING,
ACCESS_TOKEN_TYPE,
PER_PAGE_VALUE).subscribe((Consumer<? super Response<List<Headers>>>) Observable.just(Arrays.asList(mockResponse.getHeaders()));

testScheduler.triggerActions();

testObserver.awaitTerminalEvent(2, TimeUnit.SECONDS);

// No errors
testObserver.assertNoErrors();

// Make sure we made the request to the required path
assertEquals("60", mockResponse.getHeaders().get("X-RateLimit-Limit"));



// When
SUT.checkRepoPerUser(OWNER);
testScheduler.triggerActions();

// Then
then(viewMock).should().getOwner();

// Shut down the server. Instances cannot be reused.
mockWebServer.shutdown();
}

但是我得到了一个 ClassCastException:

java.lang.ClassCastException: io.reactivex.internal.operators.observable.ObservableJust cannot be cast to io.reactivex.functions.Consumer 

是否有任何可行的方法来模拟 Response(List(Headers)),或者我应该开始考虑改变从端点获取数据的方式?

最佳答案

使用 retrofit 自己的 Response 对象创建模拟响应非常简单。正如您所说,构造函数是私有(private)的,但您可以使用静态方法创建成功的响应:

Response<List<Headers>> response = Response.success(headersList);

例如,如果您想创建一个因 401 错误而失败的响应,您可以这样做:

    ResponseBody theBody = ResponseBody.create(MediaType.parse("text/html"), "");
Response<List<Headers>> response = Response.error(401, theBody);

关于android - 使用 retrofit2 和 rxjava2 对 android 应用程序进行单元测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54073274/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com