/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.example.github.repository; import com.android.example.github.api.ApiResponse; import com.android.example.github.api.GithubService; import com.android.example.github.api.RepoSearchResponse; import com.android.example.github.db.GithubDb; import com.android.example.github.db.RepoDao; import com.android.example.github.util.AbsentLiveData; import com.android.example.github.util.InstantAppExecutors; import com.android.example.github.util.TestUtil; import com.android.example.github.vo.Contributor; import com.android.example.github.vo.Repo; import com.android.example.github.vo.RepoSearchResult; import com.android.example.github.vo.Resource; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import org.mockito.ArgumentCaptor; import android.arch.core.executor.testing.InstantTaskExecutorRule; import android.arch.lifecycle.LiveData; import android.arch.lifecycle.MutableLiveData; import android.arch.lifecycle.Observer; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import retrofit2.Response; import static com.android.example.github.util.ApiUtil.successCall; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.Matchers.anyObject; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; @SuppressWarnings("unchecked") @RunWith(JUnit4.class) public class RepoRepositoryTest { private RepoRepository repository; private RepoDao dao; private GithubService service; @Rule public InstantTaskExecutorRule instantExecutorRule = new InstantTaskExecutorRule(); @Before public void init() { dao = mock(RepoDao.class); service = mock(GithubService.class); GithubDb db = mock(GithubDb.class); when(db.repoDao()).thenReturn(dao); repository = new RepoRepository(new InstantAppExecutors(), db, dao, service); } @Test public void loadRepoFromNetwork() throws IOException { MutableLiveData<Repo> dbData = new MutableLiveData<>(); when(dao.load("foo", "bar")).thenReturn(dbData); Repo repo = TestUtil.createRepo("foo", "bar", "desc"); LiveData<ApiResponse<Repo>> call = successCall(repo); when(service.getRepo("foo", "bar")).thenReturn(call); LiveData<Resource<Repo>> data = repository.loadRepo("foo", "bar"); verify(dao).load("foo", "bar"); verifyNoMoreInteractions(service); Observer observer = mock(Observer.class); data.observeForever(observer); verifyNoMoreInteractions(service); verify(observer).onChanged(Resource.loading(null)); MutableLiveData<Repo> updatedDbData = new MutableLiveData<>(); when(dao.load("foo", "bar")).thenReturn(updatedDbData); dbData.postValue(null); verify(service).getRepo("foo", "bar"); verify(dao).insert(repo); updatedDbData.postValue(repo); verify(observer).onChanged(Resource.success(repo)); } @Test public void loadContributors() throws IOException { MutableLiveData<List<Contributor>> dbData = new MutableLiveData<>(); when(dao.loadContributors("foo", "bar")).thenReturn(dbData); LiveData<Resource<List<Contributor>>> data = repository.loadContributors("foo", "bar"); verify(dao).loadContributors("foo", "bar"); verify(service, never()).getContributors(anyString(), anyString()); Repo repo = TestUtil.createRepo("foo", "bar", "desc"); Contributor contributor = TestUtil.createContributor(repo, "log", 3); // network does not send these contributor.setRepoOwner(null); contributor.setRepoName(null); List<Contributor> contributors = Collections.singletonList(contributor); LiveData<ApiResponse<List<Contributor>>> call = successCall(contributors); when(service.getContributors("foo", "bar")) .thenReturn(call); Observer<Resource<List<Contributor>>> observer = mock(Observer.class); data.observeForever(observer); verify(observer).onChanged(Resource.loading( null)); MutableLiveData<List<Contributor>> updatedDbData = new MutableLiveData<>(); when(dao.loadContributors("foo", "bar")).thenReturn(updatedDbData); dbData.setValue(Collections.emptyList()); verify(service).getContributors("foo", "bar"); ArgumentCaptor<List<Contributor>> inserted = ArgumentCaptor.forClass((Class) List.class); verify(dao).insertContributors(inserted.capture()); assertThat(inserted.getValue().size(), is(1)); Contributor first = inserted.getValue().get(0); assertThat(first.getRepoName(), is("bar")); assertThat(first.getRepoOwner(), is("foo")); updatedDbData.setValue(contributors); verify(observer).onChanged(Resource.success(contributors)); } @Test public void searchNextPage_null() { when(dao.findSearchResult("foo")).thenReturn(null); Observer<Resource<Boolean>> observer = mock(Observer.class); repository.searchNextPage("foo").observeForever(observer); verify(observer).onChanged(null); } @Test public void search_fromDb() { List<Integer> ids = Arrays.asList(1, 2); Observer<Resource<List<Repo>>> observer = mock(Observer.class); MutableLiveData<RepoSearchResult> dbSearchResult = new MutableLiveData<>(); MutableLiveData<List<Repo>> repositories = new MutableLiveData<>(); when(dao.search("foo")).thenReturn(dbSearchResult); repository.search("foo").observeForever(observer); verify(observer).onChanged(Resource.loading(null)); verifyNoMoreInteractions(service); reset(observer); RepoSearchResult dbResult = new RepoSearchResult("foo", ids, 2, null); when(dao.loadOrdered(ids)).thenReturn(repositories); dbSearchResult.postValue(dbResult); List<Repo> repoList = new ArrayList<>(); repositories.postValue(repoList); verify(observer).onChanged(Resource.success(repoList)); verifyNoMoreInteractions(service); } @Test public void search_fromServer() { List<Integer> ids = Arrays.asList(1, 2); Repo repo1 = TestUtil.createRepo(1, "owner", "repo 1", "desc 1"); Repo repo2 = TestUtil.createRepo(2, "owner", "repo 2", "desc 2"); Observer<Resource<List<Repo>>> observer = mock(Observer.class); MutableLiveData<RepoSearchResult> dbSearchResult = new MutableLiveData<>(); MutableLiveData<List<Repo>> repositories = new MutableLiveData<>(); RepoSearchResponse apiResponse = new RepoSearchResponse(); List<Repo> repoList = Arrays.asList(repo1, repo2); apiResponse.setItems(repoList); apiResponse.setTotal(2); MutableLiveData<ApiResponse<RepoSearchResponse>> callLiveData = new MutableLiveData<>(); when(service.searchRepos("foo")).thenReturn(callLiveData); when(dao.search("foo")).thenReturn(dbSearchResult); repository.search("foo").observeForever(observer); verify(observer).onChanged(Resource.loading(null)); verifyNoMoreInteractions(service); reset(observer); when(dao.loadOrdered(ids)).thenReturn(repositories); dbSearchResult.postValue(null); verify(dao, never()).loadOrdered(anyObject()); verify(service).searchRepos("foo"); MutableLiveData<RepoSearchResult> updatedResult = new MutableLiveData<>(); when(dao.search("foo")).thenReturn(updatedResult); updatedResult.postValue(new RepoSearchResult("foo", ids, 2, null)); callLiveData.postValue(new ApiResponse<>(Response.success(apiResponse))); verify(dao).insertRepos(repoList); repositories.postValue(repoList); verify(observer).onChanged(Resource.success(repoList)); verifyNoMoreInteractions(service); } @Test public void search_fromServer_error() { when(dao.search("foo")).thenReturn(AbsentLiveData.create()); MutableLiveData<ApiResponse<RepoSearchResponse>> apiResponse = new MutableLiveData<>(); when(service.searchRepos("foo")).thenReturn(apiResponse); Observer<Resource<List<Repo>>> observer = mock(Observer.class); repository.search("foo").observeForever(observer); verify(observer).onChanged(Resource.loading(null)); apiResponse.postValue(new ApiResponse<>(new Exception("idk"))); verify(observer).onChanged(Resource.error("idk", null)); } }