I've never create unit testing before. I'm planning to create UI test & Unit test for my presenter & datasource. I use Retrofit, RxJava, and Dagger in my apps.
Here's what i've tried so far
DataSource (My Datasource is coming from API)
public class DataSource implements DataSourceContract {
private static DataSource dataSource;
#Inject
SharedPreferences sharedPreferences;
#Inject
NewsService newsService;
private DataSource(Context context) {
DaggerAppComponent.builder()
.networkModule(new NetworkModule(API_URL))
.appModule(new AppModule(context.getApplicationContext()))
.preferencesModule(new PreferencesModule())
.build()
.inject(this);
}
public static synchronized DataSource getInstance(Context context) {
if(dataSource == null) {
dataSource = new DataSource(context);
}
return dataSource;
}
public String parseError(Throwable e) {
if(e instanceof SocketTimeoutException) {
return ERROR_TIMEOUT;
}
else if(e instanceof SocketException) {
return ERROR_NO_CONNECTION;
}
else {
return ERROR_SERVER;
}
}
#Override
public DisposableObserver<NewsResponse> getNews(final Callback<NewsResponse> callback) {
return newsService.getNews()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableObserver<NewsResponse>() {
#Override
public void onNext(NewsResponse value) {
callback.onSuccess(value);
}
#Override
public void onError(Throwable e) {
callback.onFailure(e);
}
#Override
public void onComplete() {
}
});
}
}
Presenter
public class MainPresenter implements MainContract.Presenter {
private MainContract.View view;
private DataSource dataSource;
private Disposable dispossable;
public MainPresenter(MainContract.View view, DataSource dataSource) {
this.view = view;
this.dataSource = dataSource;
}
#Override
public void onStart() {
getNews();
}
#Override
public void onStop() {
if(dispossable != null && !dispossable.isDisposed()) {
dispossable.dispose();
}
}
#Override
public void getNews() {
view.setLoading(true);
dispossable = dataSource.getNews(new DataSourceContract.Callback<NewsResponse>() {
#Override
public void onSuccess(NewsResponse responseData) {
try {
switch (responseData.getStatus()) {
case API_SUCCESS:
view.setLoading(false);
view.getNewsSuccess(responseData.getArticles());
break;
default:
view.setLoading(false);
view.getNewsFailed(responseData.getStatus());
break;
}
}
catch (Exception e) {
view.setLoading(false);
view.getNewsFailed(ERROR_SERVER);
}
}
#Override
public void onFailure(Throwable e) {
view.setLoading(false);
view.isNetworkFailed(dataSource.parseError(e), false);
}
});
}
}
And this is the test of my presenter
public class MainPresenterTest {
#Mock
DataSource dataSource;
#Mock
MainContract.View view;
MainContract.Presenter presenter;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
presenter = new MainPresenter(view, dataSource);
}
#Test
public void getNews() throws Exception {
List<Article> articleList = new ArrayList<>();
presenter.getNews();
Mockito.verify(view, Mockito.only()).getNewsSuccess(articleList);
}
}
But there is error when I run the test
Wanted but not invoked:
view.getNewsSuccess([]);
-> at com.java.mvp.view.main.MainPresenterTest.getNews(MainPresenterTest.java:37)
I have no problem running this apps on the device, but I can't make it work on testing
Any idea how to fix this presenter test? Am I doing it right?
And how do I test my datasource? I have no idea how to test this one
Thank you
Keep things simple. You are testing your presenter, not the data source. Add new methods to your presenter for the success and error responses. Then add two tests: one for the success and one for the error.
#Override
public void getNews() {
view.setLoading(true);
dispossable = dataSource.getNews(new DataSourceContract.Callback<NewsResponse>() {
#Override
public void onSuccess(NewsResponse responseData) {
onSuccessNewsResponse(responseData);
}
#Override
public void onFailure(Throwable e) {
onErrorNewsResponse(e);
}
});
}
Add #VisibleForTesting annotation to the new methods.
Success test:
#Test
public void getNewsSuccess() {
presenter.onSuccessNewsResponse(your_response);
Mockito.verify(...);
}
Error test:
#Test
public void getNewsError() {
presenter.onErrorNewsResponse(your_error);
Mockito.verify(...);
}
You have to mock also :
dataSource.getNews() using Mockito when :
e.g.
when(dataSource.getNews()).thenReturn(new SuccessCallback());
So you have to lead your test code into the success callback and check there what methods are called.
The same goes with the eroor case.
Related
My ViewModel class looks like this:
public class ViewModelMainActivity extends AndroidViewModel {
private LocalRepository localRepository;
private LiveData<List<Task>> allJob;
private LiveData<List<Task>> allShopping;
private LiveData<List<Task>> allOther;
public ViewModelMainActivity(#NonNull Application application) {
super(application);
localRepository = new LocalRepository(application);
allJob = localRepository.getAllJob();
allShopping = localRepository.getAllShopping();
allOther = localRepository.getAllOther();
}
public void insert(Task task) {
localRepository.insert(task);
}
public void delete(Task task) {
localRepository.delete(task);
}
public LiveData<List<Task>> getAllJob() {
return allJob;
}
public LiveData<List<Task>> getAllShopping() {
return allShopping;
}
public LiveData<List<Task>> getAllOther() {
return allOther;
}
}
Then in MainActivity calls two methods:
private void getAllJob() {
viewModelMainActivity.getAllJob().observe(this, new Observer<List<Task>>() {
#Override
public void onChanged(List<Task> tasks) {
if(tasks.size() == 0) {
linearLayoutActivityMain.setVisibility(View.VISIBLE);
} else {
linearLayoutActivityMain.setVisibility(View.INVISIBLE);
}
taskAdapter.setAllJobTasks(tasks);
}
});
}
private void getAllShopping() {
viewModelMainActivity.getAllShopping().observe(this, new Observer<List<Task>>() {
#Override
public void onChanged(List<Task> tasks) {
Log.i("Size", "Shopping: " + String.valueOf(tasks.size()));
if(tasks.size() == 0) {
linearLayoutActivityMain.setVisibility(View.VISIBLE);
} else {
linearLayoutActivityMain.setVisibility(View.INVISIBLE);
}
taskAdapter.setCurrentTasks(tasks);
}
});
}
Why when I save a task:
viewModelMainActivity.insert(task);
e.g. to the job category, both onChanged methods are called, not just the onChanged method in getAllJob.
How could I separate it? That only the onChanged method would be called for values that have changed. Should I create separate ViewModels objects? But what about saving the task then? I would have to call the insert method three times for each object?
I am writing an Android application which runs a series of back-up works.
I use workmanager to do this in the background.
Now how do I implement WorkManager in MVP ?
Below is the module for workers
#Module
public class WorkerModule {
private final Worker mworker;
public WorkerModule(Worker worker) {
mworker = worker;
}
#Provides
CompositeDisposable provideCompositeDisposable() {
return new CompositeDisposable();
}
#Provides
SchedulerProvider provideSchedulerProvider() {
return new AppSchedulerProvider();
}
////////////Presenter
}
Below is my component for Workers
#PerWorker
#Component(dependencies = ApplicationComponent.class, modules = `WorkerModule.class)
public interface WorkerComponent {
void inject(sendLocationWorker worker);
}`
Below is my Presenter
public class LocationRequestPresenter<V extends MainMvpView> extends BasePresenter<V>
implements LocationRequestMvpPresenter<V> {
private static final String TAG = "LocationRequestPresenter";
private boolean ischecked = false;
#Inject
public LocationRequestPresenter(DataManager dataManager,
SchedulerProvider schedulerProvider,
CompositeDisposable compositeDisposable) {
super(dataManager, schedulerProvider, compositeDisposable);
}
#Override
public void onGetLocations() {
getCompositeDisposable().add(getDataManager().getAllLocations()
.subscribeOn(getSchedulerProvider().io())emphasized text
.observeOn(getSchedulerProvider().ui())
.subscribe(new Consumer<List<Location>>() {
#Override
public void accept(List<Location> locationList) throws Exception {
onSendLocations(locationList);
}
}
, new Consumer<Throwable>() {
#Override
public void accept(Throwable throwable) throws Exception {
}
}));
}
#Override
public void onSendLocations(List<Location> locationList) {
getCompositeDisposable().add(getDataManager().doSendLocationHistory(new AddLocationRequest(locationList))
.subscribeOn(getSchedulerProvider().io())
.observeOn(getSchedulerProvider().ui())
.subscribe(new Consumer<CommonResponse>() {
#Override
public void accept(CommonResponse s) throws Exception {
ischecked = false;
}
}, new Consumer<Throwable>() {
#Override
public void accept(Throwable throwable) throws Exception {
}
}));
}
}
Now, why when I use Presenter, the worker returns null for presenter
for injecting Presenter I use this on my worker:
RequestPresenter<MainMvpView> mPresenter;
I'm new in android architecture component. this is my code , i'm at the point that I don't know how to notify my activity and get the results back
these are my codes:
Activity:
private void iniViewModels() {
Observer<List<User>>usersObserver=new Observer<List<User>>() {
#Override
public void onChanged(#Nullable List<User> users) {
Log.v("this","LiveData: ");
for (int i=0;i<users.size();i++){
Log.v("this",users.get(i).getName());
}
}
};
mViewModel = ViewModelProviders.of(this)//of-->name of act or fragment
.get(AcActivityViewModel.class);///get -->the name of viewModelClass
mViewModel.mUsers.observe(this,usersObserver);
}
this is my viewModel Class:
public class IpStaticViewModel extends AndroidViewModel {
public LiveData<List<Ipe>> ips;
private AppRepository repository;
public IpStaticViewModel(#NonNull Application application) {
super(application);
repository=AppRepository.getInstance(application.getApplicationContext());
}
public void getIpStatics() {
repository.getStaticIps();
}
}
this is my repository class:
public class AppRepository {
private static AppRepository ourInstance ;
private Context context;
private IpStaticInterface ipInterface;
public static AppRepository getInstance(Context context) {
if (ourInstance == null) {
ourInstance=new AppRepository(context);
}
return ourInstance;
}
private AppRepository(Context context) {
this.context=context;
}
public void getStaticIps() {
ipInterface= ApiConnection.getClient().create(IpStaticInterface.class);
ipInterface.getIpes()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new SingleObserver<IpStaticList>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onSuccess(IpStaticList ipStaticList) {
List<Ipe>ips=ipStaticList.getIpes();
}
#Override
public void onError(Throwable e) {
Log.v("this","Eror "+ e.getMessage());
}
});
}
}
I'm using retrofit for fetching the data ,it fetch the data successfully but I don't know how to notify my activity
can you help me?
Have a MutableLiveData
final MutableLiveData<List<Ipe>> data = new MutableLiveData<>();
In onSucess
public MutableLiveData<List<Ipe>> getStaticIps() {
ipInterface= ApiConnection.getClient().create(IpStaticInterface.class);
ipInterface.getIpes()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new SingleObserver<IpStaticList>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onSuccess(IpStaticList ipStaticList) {
List<Ipe>ips=ipStaticList.getIpes();
data.setValue(ips);
}
#Override
public void onError(Throwable e) {
Log.v("this","Eror "+ e.getMessage());
}
});
return data;
}
In repository expose this to viewmodel
public LiveData<List<Ipe>> getIpStatics() {
return repository.getStaticIps();
}
In Activity you observe the livedata
IpStaticViewModel viewmodel = ViewModelProviders.of(this
.get(IpStaticViewModel.class)
viewModel.getIpStatics().observe(this, new Observer<List<Ipe>>() {
#Override
public void onChanged(#Nullable List<Ipe> ipes) {
if (ipes != null) {
// dosomething
}
}
});
If you want to generalize your response in case you have a error or something have a look at https://github.com/googlesamples/android-architecture-components/blob/master/GithubBrowserSample/app/src/main/java/com/android/example/github/vo/Resource.kt
I'm developing an Android application, trying to follow Clean Architecture / MVP guidelines.
I'm currently wiriting Unit Tests for my presenters, but I'm stuck with the call to the Interactor/UseCase, that takes a DisposableObserver as a parameter.
What I would like to test is that the correct behavior is called when the interactor invokes OnNext or OnError for example (hide/show loading indicator...).
I don't know how to 'mock' the behavior of the Observable in my use Case, as it is built when the execute() method is called, using a protected method.
Below are some portions of code:
Presenter
#ConfigPersistent
public class ContentPresenter extends BasePresenter<ContentContract.View> implements ContentContract.Presenter {
#Inject
GetContent mGetContentUseCase;
#Inject
ContentViewModelMapper mContentViewModelMapper;
#Inject
public ContentPresenter() {
}
#Override
public void fetchContent(long contentId) {
getMvpView().showProgress();
mGetContentUseCase.execute(contentId, new ContentObserver());
}
private final class ContentObserver extends DisposableObserver<Content> {
#Override
public void onNext(Content content) {
getMvpView().hideProgress();
getMvpView().showContentInfo(mContentViewModelMapper.map2(content));
}
#Override
public void onError(Throwable e) {
getMvpView().hideProgress();
Timber.e(e.getMessage());
}
#Override
public void onComplete() {
getMvpView().hideProgress();
}
}
}
Interactor/UseCase
public class GetContent extends UseCaseObservableWithParameter<Long, Content, Repository> {
#Inject
public GetContent(Repository repository,
#Named("Thread") Scheduler threadScheduler,
#Named("PostExecution") Scheduler postExecutionScheduler) {
super(repository, threadScheduler, postExecutionScheduler);
}
#Override
protected Observable<Content> buildObservable(Long id) {
return repository.getContentById(id);
}
}
BaseUseCase
public abstract class UseCaseObservableWithParameter<REQUEST_DATA, RESPONSE_DATA, REPOSITORY> extends UseCase<Observable<RESPONSE_DATA>, REQUEST_DATA, REPOSITORY> {
public UseCaseObservableWithParameter(REPOSITORY repository, Scheduler threadScheduler, Scheduler postExecutionScheduler) {
super(repository, threadScheduler, postExecutionScheduler);
}
protected abstract Observable<RESPONSE_DATA> buildObservable(REQUEST_DATA requestData);
public void execute(REQUEST_DATA requestData, DisposableObserver<RESPONSE_DATA> useCaseSubscriber) {
this.disposable.clear();
this.disposable.add(
this.buildObservable(requestData)
.subscribeOn(threadScheduler)
.observeOn(postExecutionScheduler)
.subscribeWith(useCaseSubscriber)
);
}
}
After a good night's sleep, this is what I came up with:
#Test
#SuppressWarnings("unchecked")
public void testShowContents() {
doAnswer((i) -> {
// Do stuff with i.getArguments() here
DisposableObserver<Content> d = i.getArgument(1);
Observable.just(mock(Content.class)).subscribeWith(d);
return null;
})
.when(mGetContentUseCase)
.execute(eq(AppTestData.TEST_LONG_ID_1), any(DisposableObserver.class));
contentPresenter.fetchContent(AppTestData.TEST_LONG_ID_1);
Mockito.verify(view, Mockito.times(1)).showContentInfo(Mockito.any());
InOrder orderVerifier = Mockito.inOrder(view);
orderVerifier.verify(view).showProgress();
orderVerifier.verify(view).hideProgress();
orderVerifier.verify(view).showContentInfo(any());
}
#Test
#SuppressWarnings("unchecked")
public void testShowContentsError() {
doAnswer((i) -> {
// Do stuff with i.getArguments() here
DisposableObserver<Content> d = i.getArgument(1);
Observable.<Content>error(new Throwable()).subscribeWith(d);
return null;
})
.when(mGetContentUseCase)
.execute(eq(AppTestData.TEST_LONG_ID_1), any(DisposableObserver.class));
contentPresenter.fetchContent(AppTestData.TEST_LONG_ID_1);
Mockito.verify(view, Mockito.times(1)).showErrorMessage(Mockito.any());
InOrder orderVerifier = Mockito.inOrder(view);
orderVerifier.verify(view).showProgress();
orderVerifier.verify(view).hideProgress();
orderVerifier.verify(view).showErrorMessage(any());
}
I want to test a function on the Android presenter that have a callback on it. This is the function:
public void findRandomUsers() {
view.showProgress();
mDataManager.getRandomUsers(USERS_SEARCH_NUMBER, new Callback<UserList>() {
#Override
public void onResponse(Call<UserList> call, Response<UserList> response) {
if(view == null) return;
view.hideProgress();
if(response.body().getUsers().isEmpty()){
view.showIsEmptyError();
}
users = response.body();
users.setUsers(CheckRemovedUsers.avoidRemoveds(users.getUsers(), removedUsers.getRemovedUsers()));
users.setUsers(CheckDuplicatedUsers.removeDuplicated(users.getUsers()));
if(isFirstTime)
view.showUsersList(users);
else
view.updateUserList(users);
}
#Override
public void onFailure(Call<UserList> call, Throwable throwable) {
if(view == null) return;
view.hideProgress();
view.showError(throwable.getMessage());
}
});
}
The Callback is a retrofit2.Callback object USERS_SEARCH_NUMBER is an int object. If it is possible I want to control what the callback response to control if when it returns an empty response or it fails it shows the correct answer.
You have to design your achitecture the way, that you have enough seams.
Currently, you are lacking to mock the Callback<UserList>. Why won't you have
a Factory class, which provides you that callback? Then you can easily stub components.
class UserListCallbackFactory {
public UserListCallbackFactory() {}
public Callback<UserList> getCallback(Presenter presenter) {
return new Callback<UserList>() {
#Override
public void onResponse(Call<UserList> call, Response<UserList> response) {
presenter.onSuccess(response.body().getUsers());
}
#Override
public void onFailure(Call<UserList> call, Throwable throwable) {
presenter.onFailure(throwable);
}
}
}
}
Now, in the constructor of your presenter:
class Presenter extends ... {
Callback<UserList> userListCallback;
DataManager dataManager;
public Presenter(View view, UserListCallbackFactory factory, DataManager dataManager) {
...
userListCallback = factory.getCallback(this, view);
this.dataManager = dataManager;
...
}
public void onSuccess(List<User>) {
...
}
public void onFailure(Throwable e) {
...
}
}
Now you have enough seams to mock your callback in your unit test class.
#RunWith(MockitoJUnitRunner.class)
public class PresenterTest {
...
#Mock View view;
#Mock Callback<UserList> userListCallback;
#Mock UserListCallbackFactory factory;
#Mock DataManager dataManager;
#InjectMocks
Presenter presenter;
...
#Test
public void succesfulResponse() {
when(factory.getCallback(presenter)).thenReturn(userListCallback);
when(dataManager.getRandomUsers(USERS_SEARCH_NUMBER, userListCallback))
.thenAnswer(new Answer<Void>() {
#Override
public Void answer(InvocationOnMock invocation) throws Throwable {
userListCallback.onSuccess(SOME_LIST);
return null;
}
});
// check that appropriate actions are performed upon successful callback
}
}