Unit test android application with rxjava
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty{ height:90px;width:728px;box-sizing:border-box;
}
I recently jumped in Android Unit testing and I'm still struggling in writing my unit tests.
I'm trying to test my Presenter, specifically a method that returns a list of repositories from Github Api,
but I keep getting a Null Pointer Exception and I don't understand why.
RepositoriesPresenter method I want to unit test:
public void presenterLoadRepos(boolean onlineRequired, String owner) {
// Clear old data on view
view.clearRepos();
//recovering access token data from Shared Preferences
String accessTokenString = repository.getAccessTokenString();
String accessTokenTypeString = repository.getAccessTokenType();
if(onlineRequired){
Disposable disposable = repository.loadRemoteRepos(owner, accessTokenString, accessTokenTypeString, PER_PAGE_VALUE) //this is line 188
.subscribeOn(ioScheduler) //this is line 189
.observeOn(uiScheduler)
.subscribe(this::handleReturnedData, this::handleError, () -> view.stopLoadingIndicator());
disposeBag.add(disposable);
}else {
Disposable disposable = repository.loadLocalRepos(owner, accessTokenString, accessTokenTypeString, PER_PAGE_VALUE)
.subscribeOn(ioScheduler)
.observeOn(uiScheduler)
.subscribe(this::handleReturnedData, this::handleError, () -> view.stopLoadingIndicator());
disposeBag.add(disposable);
}
}
Whole RepositoriesPresenter class:
public class RepositoriesPresenter implements RepositoriesContract.Presenter, LifecycleObserver {
private static final String TAG = RepositoriesPresenter.class.getSimpleName();
private GitHubChallengeRepository repository;
private RepositoriesContract.View view;
private Scheduler ioScheduler;
private Scheduler uiScheduler;
private CompositeDisposable disposeBag;
@Inject
public RepositoriesPresenter(GitHubChallengeRepository repository, RepositoriesContract.View view,
@RunOn(IO) Scheduler ioScheduler, @RunOn(UI) Scheduler uiScheduler) {
this.repository = repository;
this.view = view;
this.ioScheduler = ioScheduler;
this.uiScheduler = uiScheduler;
// Initialize this presenter as a lifecycle-aware when a view is a lifecycle owner.
if (view instanceof LifecycleOwner) {
((LifecycleOwner) view).getLifecycle().addObserver(this);
}
disposeBag = new CompositeDisposable();
}
@Override @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) public void onAttach() {
presenterLoadRepos(false, view.getOwner());
}
@Override @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) public void onDetach() {
// Clean up any no-longer-use resources here
disposeBag.clear();
}
@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
private 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;
}
}
private void handleHeaderError(Throwable error) {
Log.e(TAG, error.getMessage(), error);
view.showErrorMessage(error.getLocalizedMessage());
}
@Override public void searchRepo(final String owner) {
view.showProgressBarIfHidden();
//recovering access token data from Shared Preferences
String accessTokenString = repository.getAccessTokenString();
String accessTokenTypeString = repository.getAccessTokenType();
// Load new one and populate it into view
Disposable disposable = repository.loadRemoteRepos(owner, accessTokenString, accessTokenTypeString, "100")
.flatMap(Observable::fromIterable)
.filter(repo -> repo.getName() != null)
.toList()
.toObservable()
.subscribeOn(ioScheduler)
.observeOn(uiScheduler)
.subscribe(repos -> {
if (repos.isEmpty()) {
// Clear old data from recycler view
view.clearRepos();
// Show notification
view.showEmptySearchResult();
} else {
// Update recycler view items
view.showRepos(repos);
}
});
disposeBag.add(disposable);
}
public void presenterLoadRepos(boolean onlineRequired, String owner) {
// Clear old data on view
view.clearRepos();
//recovering access token data from Shared Preferences
String accessTokenString = repository.getAccessTokenString();
String accessTokenTypeString = repository.getAccessTokenType();
if(onlineRequired){
Disposable disposable = repository.loadRemoteRepos(owner, accessTokenString, accessTokenTypeString, "100")
.subscribeOn(ioScheduler)
.observeOn(uiScheduler)
.subscribe(this::handleReturnedData, this::handleError, () -> view.stopLoadingIndicator());
disposeBag.add(disposable);
}else {
// Load new repositories and paginate them with 100 (GitHub API max) repositories par page.
Disposable disposable = repository.loadLocalRepos(owner, accessTokenString, accessTokenTypeString, "100")
.subscribeOn(ioScheduler)
.observeOn(uiScheduler)
.subscribe(this::handleReturnedData, this::handleError, () -> view.stopLoadingIndicator());
disposeBag.add(disposable);
}
}
/**
* Updates view after loading data is completed successfully.
*/
private void handleReturnedData(List<Repo> list) {
view.stopLoadingIndicator();
if (list != null && !list.isEmpty()) {
view.showRepos(list);
} else {
view.showNoDataMessage();
}
}
/**
* Updates view if there is an error after loading data from repository.
*/
private void handleError(Throwable error) {
if(error.getMessage().equalsIgnoreCase("http 403 forbidden")){
view.startLogin();
}else {
view.stopLoadingIndicator();
view.showErrorMessage(error.getLocalizedMessage());
}
}
@Override public void getRepo(int repoId) {
Disposable disposable = repository.getRepo(repoId)
.filter(repo -> repo != null)
.subscribeOn(ioScheduler)
.observeOn(uiScheduler)
.subscribe(repo -> view.showRepositoryDetail(repo));
disposeBag.add(disposable);
}
}
RepositoriesPresenterTest:
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.then;
//other imports omitted
@RunWith(MockitoJUnitRunner.class)
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";
@Mock private GitHubChallengeRepository repositoryMock;
@Mock private RepositoriesContract.View viewMock;
private TestScheduler testScheduler;
private RepositoriesPresenter SUT; //System Under Test
@Before public void setUp() {
MockitoAnnotations.initMocks(this);
testScheduler = new TestScheduler();
SUT = new RepositoriesPresenter(repositoryMock, viewMock, testScheduler, testScheduler);
}
@Test public void repoPresenter_reposReturned_showReposOnViewExpected() {
// Given
given(repositoryMock.loadRemoteRepos( //this is line 128
OWNER,
ACCESS_TOKEN_STRING,
ACCESS_TOKEN_TYPE,
PER_PAGE_VALUE)).willReturn(Observable.just(THREE_REPOS));
// When
SUT.presenterLoadRepos(true, OWNER); //this is line 135
testScheduler.triggerActions();
// Then
then(viewMock).should().showRepos(THREE_REPOS);
then(viewMock).should(atLeastOnce()).stopLoadingIndicator();
}
}
This is what I get when I run the test:
java.lang.NullPointerException
at link.mgiannone.githubchallenge.ui.repositories.RepositoriesPresenter.presenterLoadRepos(RepositoriesPresenter.java:189)
at link.mgiannone.githubchallenge.ui.repositories.RepositoriesPresenterTest.repoPresenter_reposReturned_showReposOnViewExpected(RepositoriesPresenterTest.java:138)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.mockito.internal.runners.DefaultInternalRunner$1.run(DefaultInternalRunner.java:68)
at org.mockito.internal.runners.DefaultInternalRunner.run(DefaultInternalRunner.java:74)
at org.mockito.internal.runners.StrictRunner.run(StrictRunner.java:39)
at org.mockito.junit.MockitoJUnitRunner.run(MockitoJUnitRunner.java:161)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMainV2.main(AppMainV2.java:131)
[MockitoHint] RepositoriesPresenterTest.repoPresenter_reposReturned_showReposOnViewExpected (see javadoc for MockitoHint):
[MockitoHint] 1. Unused... -> at link.mgiannone.githubchallenge.ui.repositories.RepositoriesPresenterTest.repoPresenter_reposReturned_showReposOnViewExpected(RepositoriesPresenterTest.java:131)
[MockitoHint] ...args ok? -> at link.mgiannone.githubchallenge.ui.repositories.RepositoriesPresenter.presenterLoadRepos(RepositoriesPresenter.java:188)
Process finished with exit code 255
from the stacktrace it seems I'm not using an argument, what am I doing wrong?

add a comment |
I recently jumped in Android Unit testing and I'm still struggling in writing my unit tests.
I'm trying to test my Presenter, specifically a method that returns a list of repositories from Github Api,
but I keep getting a Null Pointer Exception and I don't understand why.
RepositoriesPresenter method I want to unit test:
public void presenterLoadRepos(boolean onlineRequired, String owner) {
// Clear old data on view
view.clearRepos();
//recovering access token data from Shared Preferences
String accessTokenString = repository.getAccessTokenString();
String accessTokenTypeString = repository.getAccessTokenType();
if(onlineRequired){
Disposable disposable = repository.loadRemoteRepos(owner, accessTokenString, accessTokenTypeString, PER_PAGE_VALUE) //this is line 188
.subscribeOn(ioScheduler) //this is line 189
.observeOn(uiScheduler)
.subscribe(this::handleReturnedData, this::handleError, () -> view.stopLoadingIndicator());
disposeBag.add(disposable);
}else {
Disposable disposable = repository.loadLocalRepos(owner, accessTokenString, accessTokenTypeString, PER_PAGE_VALUE)
.subscribeOn(ioScheduler)
.observeOn(uiScheduler)
.subscribe(this::handleReturnedData, this::handleError, () -> view.stopLoadingIndicator());
disposeBag.add(disposable);
}
}
Whole RepositoriesPresenter class:
public class RepositoriesPresenter implements RepositoriesContract.Presenter, LifecycleObserver {
private static final String TAG = RepositoriesPresenter.class.getSimpleName();
private GitHubChallengeRepository repository;
private RepositoriesContract.View view;
private Scheduler ioScheduler;
private Scheduler uiScheduler;
private CompositeDisposable disposeBag;
@Inject
public RepositoriesPresenter(GitHubChallengeRepository repository, RepositoriesContract.View view,
@RunOn(IO) Scheduler ioScheduler, @RunOn(UI) Scheduler uiScheduler) {
this.repository = repository;
this.view = view;
this.ioScheduler = ioScheduler;
this.uiScheduler = uiScheduler;
// Initialize this presenter as a lifecycle-aware when a view is a lifecycle owner.
if (view instanceof LifecycleOwner) {
((LifecycleOwner) view).getLifecycle().addObserver(this);
}
disposeBag = new CompositeDisposable();
}
@Override @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) public void onAttach() {
presenterLoadRepos(false, view.getOwner());
}
@Override @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) public void onDetach() {
// Clean up any no-longer-use resources here
disposeBag.clear();
}
@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
private 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;
}
}
private void handleHeaderError(Throwable error) {
Log.e(TAG, error.getMessage(), error);
view.showErrorMessage(error.getLocalizedMessage());
}
@Override public void searchRepo(final String owner) {
view.showProgressBarIfHidden();
//recovering access token data from Shared Preferences
String accessTokenString = repository.getAccessTokenString();
String accessTokenTypeString = repository.getAccessTokenType();
// Load new one and populate it into view
Disposable disposable = repository.loadRemoteRepos(owner, accessTokenString, accessTokenTypeString, "100")
.flatMap(Observable::fromIterable)
.filter(repo -> repo.getName() != null)
.toList()
.toObservable()
.subscribeOn(ioScheduler)
.observeOn(uiScheduler)
.subscribe(repos -> {
if (repos.isEmpty()) {
// Clear old data from recycler view
view.clearRepos();
// Show notification
view.showEmptySearchResult();
} else {
// Update recycler view items
view.showRepos(repos);
}
});
disposeBag.add(disposable);
}
public void presenterLoadRepos(boolean onlineRequired, String owner) {
// Clear old data on view
view.clearRepos();
//recovering access token data from Shared Preferences
String accessTokenString = repository.getAccessTokenString();
String accessTokenTypeString = repository.getAccessTokenType();
if(onlineRequired){
Disposable disposable = repository.loadRemoteRepos(owner, accessTokenString, accessTokenTypeString, "100")
.subscribeOn(ioScheduler)
.observeOn(uiScheduler)
.subscribe(this::handleReturnedData, this::handleError, () -> view.stopLoadingIndicator());
disposeBag.add(disposable);
}else {
// Load new repositories and paginate them with 100 (GitHub API max) repositories par page.
Disposable disposable = repository.loadLocalRepos(owner, accessTokenString, accessTokenTypeString, "100")
.subscribeOn(ioScheduler)
.observeOn(uiScheduler)
.subscribe(this::handleReturnedData, this::handleError, () -> view.stopLoadingIndicator());
disposeBag.add(disposable);
}
}
/**
* Updates view after loading data is completed successfully.
*/
private void handleReturnedData(List<Repo> list) {
view.stopLoadingIndicator();
if (list != null && !list.isEmpty()) {
view.showRepos(list);
} else {
view.showNoDataMessage();
}
}
/**
* Updates view if there is an error after loading data from repository.
*/
private void handleError(Throwable error) {
if(error.getMessage().equalsIgnoreCase("http 403 forbidden")){
view.startLogin();
}else {
view.stopLoadingIndicator();
view.showErrorMessage(error.getLocalizedMessage());
}
}
@Override public void getRepo(int repoId) {
Disposable disposable = repository.getRepo(repoId)
.filter(repo -> repo != null)
.subscribeOn(ioScheduler)
.observeOn(uiScheduler)
.subscribe(repo -> view.showRepositoryDetail(repo));
disposeBag.add(disposable);
}
}
RepositoriesPresenterTest:
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.then;
//other imports omitted
@RunWith(MockitoJUnitRunner.class)
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";
@Mock private GitHubChallengeRepository repositoryMock;
@Mock private RepositoriesContract.View viewMock;
private TestScheduler testScheduler;
private RepositoriesPresenter SUT; //System Under Test
@Before public void setUp() {
MockitoAnnotations.initMocks(this);
testScheduler = new TestScheduler();
SUT = new RepositoriesPresenter(repositoryMock, viewMock, testScheduler, testScheduler);
}
@Test public void repoPresenter_reposReturned_showReposOnViewExpected() {
// Given
given(repositoryMock.loadRemoteRepos( //this is line 128
OWNER,
ACCESS_TOKEN_STRING,
ACCESS_TOKEN_TYPE,
PER_PAGE_VALUE)).willReturn(Observable.just(THREE_REPOS));
// When
SUT.presenterLoadRepos(true, OWNER); //this is line 135
testScheduler.triggerActions();
// Then
then(viewMock).should().showRepos(THREE_REPOS);
then(viewMock).should(atLeastOnce()).stopLoadingIndicator();
}
}
This is what I get when I run the test:
java.lang.NullPointerException
at link.mgiannone.githubchallenge.ui.repositories.RepositoriesPresenter.presenterLoadRepos(RepositoriesPresenter.java:189)
at link.mgiannone.githubchallenge.ui.repositories.RepositoriesPresenterTest.repoPresenter_reposReturned_showReposOnViewExpected(RepositoriesPresenterTest.java:138)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.mockito.internal.runners.DefaultInternalRunner$1.run(DefaultInternalRunner.java:68)
at org.mockito.internal.runners.DefaultInternalRunner.run(DefaultInternalRunner.java:74)
at org.mockito.internal.runners.StrictRunner.run(StrictRunner.java:39)
at org.mockito.junit.MockitoJUnitRunner.run(MockitoJUnitRunner.java:161)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMainV2.main(AppMainV2.java:131)
[MockitoHint] RepositoriesPresenterTest.repoPresenter_reposReturned_showReposOnViewExpected (see javadoc for MockitoHint):
[MockitoHint] 1. Unused... -> at link.mgiannone.githubchallenge.ui.repositories.RepositoriesPresenterTest.repoPresenter_reposReturned_showReposOnViewExpected(RepositoriesPresenterTest.java:131)
[MockitoHint] ...args ok? -> at link.mgiannone.githubchallenge.ui.repositories.RepositoriesPresenter.presenterLoadRepos(RepositoriesPresenter.java:188)
Process finished with exit code 255
from the stacktrace it seems I'm not using an argument, what am I doing wrong?

you gotta mock repository.getAccessTokenString()
– notTdar
Jan 4 at 6:22
Hi notTdar, I addedgiven(repositoryMock.getAccessTokenString()).willReturn(ACCESS_TOKEN_STRING)
andgiven(repositoryMock.getAccessTokenType()).willReturn(ACCESS_TOKEN_TYPE)
but I'm still getting the same error. Any other suggestion?
– makles
Jan 4 at 8:28
add a comment |
I recently jumped in Android Unit testing and I'm still struggling in writing my unit tests.
I'm trying to test my Presenter, specifically a method that returns a list of repositories from Github Api,
but I keep getting a Null Pointer Exception and I don't understand why.
RepositoriesPresenter method I want to unit test:
public void presenterLoadRepos(boolean onlineRequired, String owner) {
// Clear old data on view
view.clearRepos();
//recovering access token data from Shared Preferences
String accessTokenString = repository.getAccessTokenString();
String accessTokenTypeString = repository.getAccessTokenType();
if(onlineRequired){
Disposable disposable = repository.loadRemoteRepos(owner, accessTokenString, accessTokenTypeString, PER_PAGE_VALUE) //this is line 188
.subscribeOn(ioScheduler) //this is line 189
.observeOn(uiScheduler)
.subscribe(this::handleReturnedData, this::handleError, () -> view.stopLoadingIndicator());
disposeBag.add(disposable);
}else {
Disposable disposable = repository.loadLocalRepos(owner, accessTokenString, accessTokenTypeString, PER_PAGE_VALUE)
.subscribeOn(ioScheduler)
.observeOn(uiScheduler)
.subscribe(this::handleReturnedData, this::handleError, () -> view.stopLoadingIndicator());
disposeBag.add(disposable);
}
}
Whole RepositoriesPresenter class:
public class RepositoriesPresenter implements RepositoriesContract.Presenter, LifecycleObserver {
private static final String TAG = RepositoriesPresenter.class.getSimpleName();
private GitHubChallengeRepository repository;
private RepositoriesContract.View view;
private Scheduler ioScheduler;
private Scheduler uiScheduler;
private CompositeDisposable disposeBag;
@Inject
public RepositoriesPresenter(GitHubChallengeRepository repository, RepositoriesContract.View view,
@RunOn(IO) Scheduler ioScheduler, @RunOn(UI) Scheduler uiScheduler) {
this.repository = repository;
this.view = view;
this.ioScheduler = ioScheduler;
this.uiScheduler = uiScheduler;
// Initialize this presenter as a lifecycle-aware when a view is a lifecycle owner.
if (view instanceof LifecycleOwner) {
((LifecycleOwner) view).getLifecycle().addObserver(this);
}
disposeBag = new CompositeDisposable();
}
@Override @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) public void onAttach() {
presenterLoadRepos(false, view.getOwner());
}
@Override @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) public void onDetach() {
// Clean up any no-longer-use resources here
disposeBag.clear();
}
@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
private 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;
}
}
private void handleHeaderError(Throwable error) {
Log.e(TAG, error.getMessage(), error);
view.showErrorMessage(error.getLocalizedMessage());
}
@Override public void searchRepo(final String owner) {
view.showProgressBarIfHidden();
//recovering access token data from Shared Preferences
String accessTokenString = repository.getAccessTokenString();
String accessTokenTypeString = repository.getAccessTokenType();
// Load new one and populate it into view
Disposable disposable = repository.loadRemoteRepos(owner, accessTokenString, accessTokenTypeString, "100")
.flatMap(Observable::fromIterable)
.filter(repo -> repo.getName() != null)
.toList()
.toObservable()
.subscribeOn(ioScheduler)
.observeOn(uiScheduler)
.subscribe(repos -> {
if (repos.isEmpty()) {
// Clear old data from recycler view
view.clearRepos();
// Show notification
view.showEmptySearchResult();
} else {
// Update recycler view items
view.showRepos(repos);
}
});
disposeBag.add(disposable);
}
public void presenterLoadRepos(boolean onlineRequired, String owner) {
// Clear old data on view
view.clearRepos();
//recovering access token data from Shared Preferences
String accessTokenString = repository.getAccessTokenString();
String accessTokenTypeString = repository.getAccessTokenType();
if(onlineRequired){
Disposable disposable = repository.loadRemoteRepos(owner, accessTokenString, accessTokenTypeString, "100")
.subscribeOn(ioScheduler)
.observeOn(uiScheduler)
.subscribe(this::handleReturnedData, this::handleError, () -> view.stopLoadingIndicator());
disposeBag.add(disposable);
}else {
// Load new repositories and paginate them with 100 (GitHub API max) repositories par page.
Disposable disposable = repository.loadLocalRepos(owner, accessTokenString, accessTokenTypeString, "100")
.subscribeOn(ioScheduler)
.observeOn(uiScheduler)
.subscribe(this::handleReturnedData, this::handleError, () -> view.stopLoadingIndicator());
disposeBag.add(disposable);
}
}
/**
* Updates view after loading data is completed successfully.
*/
private void handleReturnedData(List<Repo> list) {
view.stopLoadingIndicator();
if (list != null && !list.isEmpty()) {
view.showRepos(list);
} else {
view.showNoDataMessage();
}
}
/**
* Updates view if there is an error after loading data from repository.
*/
private void handleError(Throwable error) {
if(error.getMessage().equalsIgnoreCase("http 403 forbidden")){
view.startLogin();
}else {
view.stopLoadingIndicator();
view.showErrorMessage(error.getLocalizedMessage());
}
}
@Override public void getRepo(int repoId) {
Disposable disposable = repository.getRepo(repoId)
.filter(repo -> repo != null)
.subscribeOn(ioScheduler)
.observeOn(uiScheduler)
.subscribe(repo -> view.showRepositoryDetail(repo));
disposeBag.add(disposable);
}
}
RepositoriesPresenterTest:
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.then;
//other imports omitted
@RunWith(MockitoJUnitRunner.class)
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";
@Mock private GitHubChallengeRepository repositoryMock;
@Mock private RepositoriesContract.View viewMock;
private TestScheduler testScheduler;
private RepositoriesPresenter SUT; //System Under Test
@Before public void setUp() {
MockitoAnnotations.initMocks(this);
testScheduler = new TestScheduler();
SUT = new RepositoriesPresenter(repositoryMock, viewMock, testScheduler, testScheduler);
}
@Test public void repoPresenter_reposReturned_showReposOnViewExpected() {
// Given
given(repositoryMock.loadRemoteRepos( //this is line 128
OWNER,
ACCESS_TOKEN_STRING,
ACCESS_TOKEN_TYPE,
PER_PAGE_VALUE)).willReturn(Observable.just(THREE_REPOS));
// When
SUT.presenterLoadRepos(true, OWNER); //this is line 135
testScheduler.triggerActions();
// Then
then(viewMock).should().showRepos(THREE_REPOS);
then(viewMock).should(atLeastOnce()).stopLoadingIndicator();
}
}
This is what I get when I run the test:
java.lang.NullPointerException
at link.mgiannone.githubchallenge.ui.repositories.RepositoriesPresenter.presenterLoadRepos(RepositoriesPresenter.java:189)
at link.mgiannone.githubchallenge.ui.repositories.RepositoriesPresenterTest.repoPresenter_reposReturned_showReposOnViewExpected(RepositoriesPresenterTest.java:138)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.mockito.internal.runners.DefaultInternalRunner$1.run(DefaultInternalRunner.java:68)
at org.mockito.internal.runners.DefaultInternalRunner.run(DefaultInternalRunner.java:74)
at org.mockito.internal.runners.StrictRunner.run(StrictRunner.java:39)
at org.mockito.junit.MockitoJUnitRunner.run(MockitoJUnitRunner.java:161)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMainV2.main(AppMainV2.java:131)
[MockitoHint] RepositoriesPresenterTest.repoPresenter_reposReturned_showReposOnViewExpected (see javadoc for MockitoHint):
[MockitoHint] 1. Unused... -> at link.mgiannone.githubchallenge.ui.repositories.RepositoriesPresenterTest.repoPresenter_reposReturned_showReposOnViewExpected(RepositoriesPresenterTest.java:131)
[MockitoHint] ...args ok? -> at link.mgiannone.githubchallenge.ui.repositories.RepositoriesPresenter.presenterLoadRepos(RepositoriesPresenter.java:188)
Process finished with exit code 255
from the stacktrace it seems I'm not using an argument, what am I doing wrong?

I recently jumped in Android Unit testing and I'm still struggling in writing my unit tests.
I'm trying to test my Presenter, specifically a method that returns a list of repositories from Github Api,
but I keep getting a Null Pointer Exception and I don't understand why.
RepositoriesPresenter method I want to unit test:
public void presenterLoadRepos(boolean onlineRequired, String owner) {
// Clear old data on view
view.clearRepos();
//recovering access token data from Shared Preferences
String accessTokenString = repository.getAccessTokenString();
String accessTokenTypeString = repository.getAccessTokenType();
if(onlineRequired){
Disposable disposable = repository.loadRemoteRepos(owner, accessTokenString, accessTokenTypeString, PER_PAGE_VALUE) //this is line 188
.subscribeOn(ioScheduler) //this is line 189
.observeOn(uiScheduler)
.subscribe(this::handleReturnedData, this::handleError, () -> view.stopLoadingIndicator());
disposeBag.add(disposable);
}else {
Disposable disposable = repository.loadLocalRepos(owner, accessTokenString, accessTokenTypeString, PER_PAGE_VALUE)
.subscribeOn(ioScheduler)
.observeOn(uiScheduler)
.subscribe(this::handleReturnedData, this::handleError, () -> view.stopLoadingIndicator());
disposeBag.add(disposable);
}
}
Whole RepositoriesPresenter class:
public class RepositoriesPresenter implements RepositoriesContract.Presenter, LifecycleObserver {
private static final String TAG = RepositoriesPresenter.class.getSimpleName();
private GitHubChallengeRepository repository;
private RepositoriesContract.View view;
private Scheduler ioScheduler;
private Scheduler uiScheduler;
private CompositeDisposable disposeBag;
@Inject
public RepositoriesPresenter(GitHubChallengeRepository repository, RepositoriesContract.View view,
@RunOn(IO) Scheduler ioScheduler, @RunOn(UI) Scheduler uiScheduler) {
this.repository = repository;
this.view = view;
this.ioScheduler = ioScheduler;
this.uiScheduler = uiScheduler;
// Initialize this presenter as a lifecycle-aware when a view is a lifecycle owner.
if (view instanceof LifecycleOwner) {
((LifecycleOwner) view).getLifecycle().addObserver(this);
}
disposeBag = new CompositeDisposable();
}
@Override @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) public void onAttach() {
presenterLoadRepos(false, view.getOwner());
}
@Override @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) public void onDetach() {
// Clean up any no-longer-use resources here
disposeBag.clear();
}
@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
private 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;
}
}
private void handleHeaderError(Throwable error) {
Log.e(TAG, error.getMessage(), error);
view.showErrorMessage(error.getLocalizedMessage());
}
@Override public void searchRepo(final String owner) {
view.showProgressBarIfHidden();
//recovering access token data from Shared Preferences
String accessTokenString = repository.getAccessTokenString();
String accessTokenTypeString = repository.getAccessTokenType();
// Load new one and populate it into view
Disposable disposable = repository.loadRemoteRepos(owner, accessTokenString, accessTokenTypeString, "100")
.flatMap(Observable::fromIterable)
.filter(repo -> repo.getName() != null)
.toList()
.toObservable()
.subscribeOn(ioScheduler)
.observeOn(uiScheduler)
.subscribe(repos -> {
if (repos.isEmpty()) {
// Clear old data from recycler view
view.clearRepos();
// Show notification
view.showEmptySearchResult();
} else {
// Update recycler view items
view.showRepos(repos);
}
});
disposeBag.add(disposable);
}
public void presenterLoadRepos(boolean onlineRequired, String owner) {
// Clear old data on view
view.clearRepos();
//recovering access token data from Shared Preferences
String accessTokenString = repository.getAccessTokenString();
String accessTokenTypeString = repository.getAccessTokenType();
if(onlineRequired){
Disposable disposable = repository.loadRemoteRepos(owner, accessTokenString, accessTokenTypeString, "100")
.subscribeOn(ioScheduler)
.observeOn(uiScheduler)
.subscribe(this::handleReturnedData, this::handleError, () -> view.stopLoadingIndicator());
disposeBag.add(disposable);
}else {
// Load new repositories and paginate them with 100 (GitHub API max) repositories par page.
Disposable disposable = repository.loadLocalRepos(owner, accessTokenString, accessTokenTypeString, "100")
.subscribeOn(ioScheduler)
.observeOn(uiScheduler)
.subscribe(this::handleReturnedData, this::handleError, () -> view.stopLoadingIndicator());
disposeBag.add(disposable);
}
}
/**
* Updates view after loading data is completed successfully.
*/
private void handleReturnedData(List<Repo> list) {
view.stopLoadingIndicator();
if (list != null && !list.isEmpty()) {
view.showRepos(list);
} else {
view.showNoDataMessage();
}
}
/**
* Updates view if there is an error after loading data from repository.
*/
private void handleError(Throwable error) {
if(error.getMessage().equalsIgnoreCase("http 403 forbidden")){
view.startLogin();
}else {
view.stopLoadingIndicator();
view.showErrorMessage(error.getLocalizedMessage());
}
}
@Override public void getRepo(int repoId) {
Disposable disposable = repository.getRepo(repoId)
.filter(repo -> repo != null)
.subscribeOn(ioScheduler)
.observeOn(uiScheduler)
.subscribe(repo -> view.showRepositoryDetail(repo));
disposeBag.add(disposable);
}
}
RepositoriesPresenterTest:
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.then;
//other imports omitted
@RunWith(MockitoJUnitRunner.class)
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";
@Mock private GitHubChallengeRepository repositoryMock;
@Mock private RepositoriesContract.View viewMock;
private TestScheduler testScheduler;
private RepositoriesPresenter SUT; //System Under Test
@Before public void setUp() {
MockitoAnnotations.initMocks(this);
testScheduler = new TestScheduler();
SUT = new RepositoriesPresenter(repositoryMock, viewMock, testScheduler, testScheduler);
}
@Test public void repoPresenter_reposReturned_showReposOnViewExpected() {
// Given
given(repositoryMock.loadRemoteRepos( //this is line 128
OWNER,
ACCESS_TOKEN_STRING,
ACCESS_TOKEN_TYPE,
PER_PAGE_VALUE)).willReturn(Observable.just(THREE_REPOS));
// When
SUT.presenterLoadRepos(true, OWNER); //this is line 135
testScheduler.triggerActions();
// Then
then(viewMock).should().showRepos(THREE_REPOS);
then(viewMock).should(atLeastOnce()).stopLoadingIndicator();
}
}
This is what I get when I run the test:
java.lang.NullPointerException
at link.mgiannone.githubchallenge.ui.repositories.RepositoriesPresenter.presenterLoadRepos(RepositoriesPresenter.java:189)
at link.mgiannone.githubchallenge.ui.repositories.RepositoriesPresenterTest.repoPresenter_reposReturned_showReposOnViewExpected(RepositoriesPresenterTest.java:138)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.mockito.internal.runners.DefaultInternalRunner$1.run(DefaultInternalRunner.java:68)
at org.mockito.internal.runners.DefaultInternalRunner.run(DefaultInternalRunner.java:74)
at org.mockito.internal.runners.StrictRunner.run(StrictRunner.java:39)
at org.mockito.junit.MockitoJUnitRunner.run(MockitoJUnitRunner.java:161)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMainV2.main(AppMainV2.java:131)
[MockitoHint] RepositoriesPresenterTest.repoPresenter_reposReturned_showReposOnViewExpected (see javadoc for MockitoHint):
[MockitoHint] 1. Unused... -> at link.mgiannone.githubchallenge.ui.repositories.RepositoriesPresenterTest.repoPresenter_reposReturned_showReposOnViewExpected(RepositoriesPresenterTest.java:131)
[MockitoHint] ...args ok? -> at link.mgiannone.githubchallenge.ui.repositories.RepositoriesPresenter.presenterLoadRepos(RepositoriesPresenter.java:188)
Process finished with exit code 255
from the stacktrace it seems I'm not using an argument, what am I doing wrong?


edited Jan 7 at 13:28
makles
asked Jan 3 at 10:51
maklesmakles
2017
2017
you gotta mock repository.getAccessTokenString()
– notTdar
Jan 4 at 6:22
Hi notTdar, I addedgiven(repositoryMock.getAccessTokenString()).willReturn(ACCESS_TOKEN_STRING)
andgiven(repositoryMock.getAccessTokenType()).willReturn(ACCESS_TOKEN_TYPE)
but I'm still getting the same error. Any other suggestion?
– makles
Jan 4 at 8:28
add a comment |
you gotta mock repository.getAccessTokenString()
– notTdar
Jan 4 at 6:22
Hi notTdar, I addedgiven(repositoryMock.getAccessTokenString()).willReturn(ACCESS_TOKEN_STRING)
andgiven(repositoryMock.getAccessTokenType()).willReturn(ACCESS_TOKEN_TYPE)
but I'm still getting the same error. Any other suggestion?
– makles
Jan 4 at 8:28
you gotta mock repository.getAccessTokenString()
– notTdar
Jan 4 at 6:22
you gotta mock repository.getAccessTokenString()
– notTdar
Jan 4 at 6:22
Hi notTdar, I added
given(repositoryMock.getAccessTokenString()).willReturn(ACCESS_TOKEN_STRING)
and given(repositoryMock.getAccessTokenType()).willReturn(ACCESS_TOKEN_TYPE)
but I'm still getting the same error. Any other suggestion?– makles
Jan 4 at 8:28
Hi notTdar, I added
given(repositoryMock.getAccessTokenString()).willReturn(ACCESS_TOKEN_STRING)
and given(repositoryMock.getAccessTokenType()).willReturn(ACCESS_TOKEN_TYPE)
but I'm still getting the same error. Any other suggestion?– makles
Jan 4 at 8:28
add a comment |
2 Answers
2
active
oldest
votes
In your presenter, loadRemoteRepos
is called with a perPageValue
of "100"
, but in your test, the given
part of the repositoryMock
only matches the parameter value of "per_page_value"
.
Either match the last parameter with anyString
(in this case all the other parameters should be wrapped in an eq
matcher), or use the same value in the test as in the presenter code, or inject the value through the constructor of the presenter.
It works now. Thank you.
– makles
Jan 8 at 9:33
add a comment |
as your repository is a mocked instance, you'll need to mock the responses for all of its methods. This includes these two:
String accessTokenString = repository.getAccessTokenString();
String accessTokenTypeString = repository.getAccessTokenType();
So you need given statements for them as well. Something on the lines:
given(repositoryMock.getAccessTokenString()).willReturn("A string")
given(repositoryMock.getAccessTokenType()).willReturn("A string")
I added those 2 given statements but I still getting the same error.
– makles
Jan 3 at 11:52
What did you add instead of "a string"?
– Levi Moreira
Jan 3 at 11:58
1
I added the same constants used in the third given statement: ACCESS_TOKEN_STRING and ACCESS_TOKEN_TYPE
– makles
Jan 3 at 12:01
could you post your presenter complete code?
– Levi Moreira
Jan 7 at 13:23
Post updated with the whole RepositoriesPresenter class
– makles
Jan 7 at 13:29
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54020865%2funit-test-android-application-with-rxjava%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
In your presenter, loadRemoteRepos
is called with a perPageValue
of "100"
, but in your test, the given
part of the repositoryMock
only matches the parameter value of "per_page_value"
.
Either match the last parameter with anyString
(in this case all the other parameters should be wrapped in an eq
matcher), or use the same value in the test as in the presenter code, or inject the value through the constructor of the presenter.
It works now. Thank you.
– makles
Jan 8 at 9:33
add a comment |
In your presenter, loadRemoteRepos
is called with a perPageValue
of "100"
, but in your test, the given
part of the repositoryMock
only matches the parameter value of "per_page_value"
.
Either match the last parameter with anyString
(in this case all the other parameters should be wrapped in an eq
matcher), or use the same value in the test as in the presenter code, or inject the value through the constructor of the presenter.
It works now. Thank you.
– makles
Jan 8 at 9:33
add a comment |
In your presenter, loadRemoteRepos
is called with a perPageValue
of "100"
, but in your test, the given
part of the repositoryMock
only matches the parameter value of "per_page_value"
.
Either match the last parameter with anyString
(in this case all the other parameters should be wrapped in an eq
matcher), or use the same value in the test as in the presenter code, or inject the value through the constructor of the presenter.
In your presenter, loadRemoteRepos
is called with a perPageValue
of "100"
, but in your test, the given
part of the repositoryMock
only matches the parameter value of "per_page_value"
.
Either match the last parameter with anyString
(in this case all the other parameters should be wrapped in an eq
matcher), or use the same value in the test as in the presenter code, or inject the value through the constructor of the presenter.
answered Jan 8 at 9:20


Janos BreuerJanos Breuer
37115
37115
It works now. Thank you.
– makles
Jan 8 at 9:33
add a comment |
It works now. Thank you.
– makles
Jan 8 at 9:33
It works now. Thank you.
– makles
Jan 8 at 9:33
It works now. Thank you.
– makles
Jan 8 at 9:33
add a comment |
as your repository is a mocked instance, you'll need to mock the responses for all of its methods. This includes these two:
String accessTokenString = repository.getAccessTokenString();
String accessTokenTypeString = repository.getAccessTokenType();
So you need given statements for them as well. Something on the lines:
given(repositoryMock.getAccessTokenString()).willReturn("A string")
given(repositoryMock.getAccessTokenType()).willReturn("A string")
I added those 2 given statements but I still getting the same error.
– makles
Jan 3 at 11:52
What did you add instead of "a string"?
– Levi Moreira
Jan 3 at 11:58
1
I added the same constants used in the third given statement: ACCESS_TOKEN_STRING and ACCESS_TOKEN_TYPE
– makles
Jan 3 at 12:01
could you post your presenter complete code?
– Levi Moreira
Jan 7 at 13:23
Post updated with the whole RepositoriesPresenter class
– makles
Jan 7 at 13:29
add a comment |
as your repository is a mocked instance, you'll need to mock the responses for all of its methods. This includes these two:
String accessTokenString = repository.getAccessTokenString();
String accessTokenTypeString = repository.getAccessTokenType();
So you need given statements for them as well. Something on the lines:
given(repositoryMock.getAccessTokenString()).willReturn("A string")
given(repositoryMock.getAccessTokenType()).willReturn("A string")
I added those 2 given statements but I still getting the same error.
– makles
Jan 3 at 11:52
What did you add instead of "a string"?
– Levi Moreira
Jan 3 at 11:58
1
I added the same constants used in the third given statement: ACCESS_TOKEN_STRING and ACCESS_TOKEN_TYPE
– makles
Jan 3 at 12:01
could you post your presenter complete code?
– Levi Moreira
Jan 7 at 13:23
Post updated with the whole RepositoriesPresenter class
– makles
Jan 7 at 13:29
add a comment |
as your repository is a mocked instance, you'll need to mock the responses for all of its methods. This includes these two:
String accessTokenString = repository.getAccessTokenString();
String accessTokenTypeString = repository.getAccessTokenType();
So you need given statements for them as well. Something on the lines:
given(repositoryMock.getAccessTokenString()).willReturn("A string")
given(repositoryMock.getAccessTokenType()).willReturn("A string")
as your repository is a mocked instance, you'll need to mock the responses for all of its methods. This includes these two:
String accessTokenString = repository.getAccessTokenString();
String accessTokenTypeString = repository.getAccessTokenType();
So you need given statements for them as well. Something on the lines:
given(repositoryMock.getAccessTokenString()).willReturn("A string")
given(repositoryMock.getAccessTokenType()).willReturn("A string")
answered Jan 3 at 11:13


Levi MoreiraLevi Moreira
8,55331735
8,55331735
I added those 2 given statements but I still getting the same error.
– makles
Jan 3 at 11:52
What did you add instead of "a string"?
– Levi Moreira
Jan 3 at 11:58
1
I added the same constants used in the third given statement: ACCESS_TOKEN_STRING and ACCESS_TOKEN_TYPE
– makles
Jan 3 at 12:01
could you post your presenter complete code?
– Levi Moreira
Jan 7 at 13:23
Post updated with the whole RepositoriesPresenter class
– makles
Jan 7 at 13:29
add a comment |
I added those 2 given statements but I still getting the same error.
– makles
Jan 3 at 11:52
What did you add instead of "a string"?
– Levi Moreira
Jan 3 at 11:58
1
I added the same constants used in the third given statement: ACCESS_TOKEN_STRING and ACCESS_TOKEN_TYPE
– makles
Jan 3 at 12:01
could you post your presenter complete code?
– Levi Moreira
Jan 7 at 13:23
Post updated with the whole RepositoriesPresenter class
– makles
Jan 7 at 13:29
I added those 2 given statements but I still getting the same error.
– makles
Jan 3 at 11:52
I added those 2 given statements but I still getting the same error.
– makles
Jan 3 at 11:52
What did you add instead of "a string"?
– Levi Moreira
Jan 3 at 11:58
What did you add instead of "a string"?
– Levi Moreira
Jan 3 at 11:58
1
1
I added the same constants used in the third given statement: ACCESS_TOKEN_STRING and ACCESS_TOKEN_TYPE
– makles
Jan 3 at 12:01
I added the same constants used in the third given statement: ACCESS_TOKEN_STRING and ACCESS_TOKEN_TYPE
– makles
Jan 3 at 12:01
could you post your presenter complete code?
– Levi Moreira
Jan 7 at 13:23
could you post your presenter complete code?
– Levi Moreira
Jan 7 at 13:23
Post updated with the whole RepositoriesPresenter class
– makles
Jan 7 at 13:29
Post updated with the whole RepositoriesPresenter class
– makles
Jan 7 at 13:29
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54020865%2funit-test-android-application-with-rxjava%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
you gotta mock repository.getAccessTokenString()
– notTdar
Jan 4 at 6:22
Hi notTdar, I added
given(repositoryMock.getAccessTokenString()).willReturn(ACCESS_TOKEN_STRING)
andgiven(repositoryMock.getAccessTokenType()).willReturn(ACCESS_TOKEN_TYPE)
but I'm still getting the same error. Any other suggestion?– makles
Jan 4 at 8:28