How to unit test observer called from a private method? - java

I have a view model class, SampleViewModel, that has an observer, DataSourceObserver, whose event's (e.g. onDataUpdated) are triggered from a private method of the view model's data source class.
I am trying to add a unit test for how the view model's observer handles the onDataUpdated.
#HiltViewModel
public class SampleViewModel extends ViewModel {
private final #NonNull ViewModelDataSource viewModelDataSource;
private final #NonNull ViewModelDataSource.DataSourceObserver observer = new ViewModelDataSource.DataSourceObserver() {
#Overrode
public void onDataUpdated(){
// some sort of handling
}
};
#Inject
public SampleViewModel(final #NonNull ViewModelDataSource dataSource){
viewModelDataSource = dataSource;
viewModelDataSource.setObserver(observer);
}
}
public class ViewModelDataSource{
public interface DataSourceObserver {
void onDataUpdated();
}
private final ContactObservers.Observer contactObserver = new ContactObservers.Observer(){
#Override
public void onContactUpdated(){
if (myObserver != null){
myObserver.onDataUpdated();
}
}
};
private #Nullable DataSourceObserver myObserver;
#Inject
public ViewModelDataSource(){
// other initialization here
}
public void setObserver(DataSourceObserver observer){
myObserver = observer;
}
}
I have Mockito for mocking/stubbing objects, but I have no idea how to approach such a problem so that I can at least trigger something like observer.onDataUpdated and verify the method calls within the handling of onDataUpdated

Found a solution to use ArgumentCaptor to capture the initialized observer in the view model when viewModelDataSource.setObserver(observer); is called.
In my unit test:
ArgumentCaptor<ViewModelDataSource.DataSourceObserver> capturedObserver = ArgumentCaptor.forClass(ViewModelDataSource.DataSourceObserver.class);
SampleViewModel viewModel = new SimpleViewModel( ...);
assertNotNull(capturedObserver);

Related

How do I know what changed in my Observable class?

So I'm using the Observer pattern in my app in order to get notified of changes in another class without having to look for them.
I have a Singleton class which extends Observable. Inside this class I have two CountDownTimer type variables. Eachs of these contains two methods: onTick() and onFinished().
Let's call those Timers A and B for the sake of simplicity.
Every time A.onTick(), A.onFinished(), B.onTick(), B.onFinished() are called, I must call notifyObservers() to notify my Observer that something has changed.
Until here everything works fine. The problem is that I know something has changed, but I don't know what exactly has changed. Depending on which one notified me, I must execute some code on the Observer side.
How do I know which of these methods notified me?
Use LiveData instead of Observable. LiveData is quite useful because not only it's observable but also it binds to your activity's lifecycle so you don't have to worry about handling it yourself.
Maybe this example will help you:
public class MyTimerWrapper {
public static MyTimerWrapper getInstance() {
// Your singleton logic
createTimers();
return instance;
}
private CountDownTimer timerA;
private CountDownTimer timerB;
private MutableLiveData<TimerEvent> timerALiveData = new MutableLiveData<TimerEvent>();
private MutableLiveData<TimerEvent> timerBLiveData = new MutableLiveData<TimerEvent>();
public LiveData<TimerEvent> startTimerA() {
timerA.start();
return timerALiveData;
}
public LiveData<TimerEvent> startTimerB() {
timerB.start();
return timerBLiveData;
}
private void createTimers() {
createTimerA();
createTimerB();
}
private void createTimerA() {
timerA = new CountDownTimer(30000, 1000) {
#Override
public void onTick(long millisUntilFinished) {
// If you're running on another thread
timerALiveData.postValue(TimerEvent.TICK);
// Otherwise
timerALiveData.setValue(TimerEvent.TICK);
}
#Override
public void onFinish() {
// If you're running on another thread
timerALiveData.postValue(TimerEvent.FINISH);
// Otherwise
timerALiveData.setValue(TimerEvent.FINISH);
}
}
}
private void createTimerB() {
// Same as createTimerA, but with timerB
}
}
public enum TimerEvent {
TICK,
FINISH
}
Now to observe that data in your activity:
MyTimerWrapper timerWrapper = MyTimerWrapper.getInstance();
timerWrapper.startTimerA().observe(this, new Observer {
#Override
public void onChanged(TimerEvent timerEvent) {
// Here you'll be able to see whether timerA is ticking or finished
}
})
You can create a custom EventType class and pass it to Observable.notifyObservers(Object arg):
public class EventType {
String eventType; //"onTick" or "onFinish"
TimerType timerType;
EventType(String eventType, TimerType timerType){
this.eventType = eventType;
this.timerType = timerType;
}
}
TimerType is an enum type:
public enum TimerType {
A,
B;
}
and create TimerA and TimerB classes extending CountDownTimer:
private class TimerA extends CountDownTimer {
final EventType onTickEvent = new EventType("onTick", TimerType.A);
final EventType onFinishEvent = new EventType("onFinish", TimerType.A);
#Override
public void onTick(long millisUntilFinished) {
notifyObservers(onTickEvent);
}
#Override
public void onFinish() {
notifyObservers(onFinishEvent)
}
}
The Observerwill receive the EventType instance via its update(Observable o, Object arg); in the arg argument

How to make Room database insert method return int through mvvm architecture?

I want to get the id auto-generated while performing insert operation on Room database. I am implementing MVVM (Model-View-ViewModel) architecture which makes use of DAO to fire queries to Room database. I have added a repository layer between viewmodel and DAO to create an AsyncTask to perform database operations. How do I get the output of insert operation (which is the inserted row's auto-generated id) to the fragment that uses the viewmodel. The layers are as follows: Fragment -> ViewModel -> Repository -> DAO
ListFragment.java
public class ListFragment extends Fragment {
private ReminderViewModel viewModel;
private int id;
...
viewModel = ViewModelProviders.of(this).get(ReminderViewModel.class);
...
id = viewModel.insert(new TodoReminder(0, description, date, time));
...
}
ReminderViewModel.java
public class ReminderViewModel extends AndroidViewModel {
private ReminderRepository repository;
public ReminderViewModel(#NonNull Application application) {
super(application);
repository = new ReminderRepository(application);
}
public int insert(TodoReminder reminder) {
repository.insert(reminder);
}
}
ReminderRepository.java
public class ReminderRepository {
private ReminderDAO reminderDAO;
public ReminderRepository(Application application) {
AppDatabase db = AppDatabase.getDatabase(application);
reminderDAO = db.getReminderDAO();
}
public int insert(TodoReminder reminder) {
new insertAsyncTask(reminderDAO).execute(reminder);
}
private static class InsertAsyncTask extends AsyncTask<TodoReminder, Void, Integer> {
private ReminderDAO asyncTaskDAO;
insertAsyncTask(ReminderDAO dao) {
asyncTaskDAO = dao;
}
#Override
protected Integer doInBackground(final TodoReminder... reminders) {
return asyncTaskDAO.insert(reminders[0]);
}
}
}
ReminderDAO.java
#Dao
public interface ReminderDAO {
#Insert
public int insert(TodoReminder... reminders);
}
ToDoReminder.java
public class TodoReminder implements Serializable {
#PrimaryKey(autoGenerate = true)
#NonNull
private int id;
...
}
How should I get the int returned from the insert method of ReminderDAO and return it from the insert method in ReminderRepository?
You can create your database table in such a way that the id is incremented automatically. In MySQL that is done via the auto_increment keyword. In SQL Server it is done via the identity(1, 1) syntax. In Access it is the autoincrement keyword. In Oracle and PostgreSQL it is done using sequences. If you manage to do this, then you will not need to manually work on incrementing these values. If, for some reason this is out of the question, then you can create a before insert trigger which will load the maximum id and add 1 to it, storing the result in the id. Or, if even that is out of the question, then you can load the last ID, but that has a different syntax in different databases. Or, you can run a query like this:
select max(id) + 1 from yourtable;
but beware possible performance issues and concurrency problems.
I have the exact problem. This is what I did.
I created an interface
public interface Task
{
void processInsert(long id)
}
supply the interface to the insert fcn in the repository
public class Repository {
private Dao myDao;
public void insertMyObject(MyOBject object,Task myInterface ) {
new insertAysncTask(myDao,myInterface).execute(object);
}
private static class insertAsyncTask extends AysncTask<MyObject,Void,Long>{
private Dao mDao;
private Task mTask;
public insertAysncTask(Dao dao, Task task) {
this.mDao = dao;
this.mTask=task;
}
#Override
protected Long doInBackground(MyObject... myObjects) {
return mDao.insertMyObject(myObjects[0]);
}
#Override
protected void onPostExecute(Long aLong) {
super.onPostExecute(aLong);
mTask.processInsert(aLong);
}
}
}
in the DAO class fcn should have return type of Long
public interface MyDao {
#Insert
Long insertMyObject(MyObject object);
have the ViewModel implement the interface
public class MyObjectViewModel extends AndroidViewModel implements Task{
private Repository mRepository;
public MyObjectViewModel(#NonNull Application application) {
super(application);
mRepository = new Repository(application);
}
#Override
public void processInsert(Long id) {
// code for processing the id returned from the db insert
}
public void insertMyObject(MyObject object){
mRepository.insertMyObject(object,this);}
}
in the activity/fragment call the ViewModel insert
mViewModel.insertMyObject(object);
Update
If you want to return the id to the activity or fragment then have the fragment/activity implement the interface instead of the viewmodel
ListFragment extends Fragment implements Task{
.....
#Override
public void processInsert(Long id){
//process id here
}
//call the insert fcn passing in the reference to the fragment
mViewModel.insertMyObject(object,this)
}
Modify the insert fcn in the View Model to accept a reference to the interface
public void insertMyObject(MyObject object, Task myInterface){
mRepository.insertMyObject(object,myInterface);
}
with this approach, the asynctask in the repository insert fcn will hold a reference to the activity/fragment and if the activity/fragment is destroyed before the asynctask finishes this will be a problem. I think it's better to do the processing in the viewmodel if possible. Fragments/activities should only deal with UI concerns not data processing.
Alternative Method
An alternative would be to use a LiveData with an observer.
public class Repository{
private Dao myDao;
private MutableLiveData<Long> dbInsertId = new MutableLiveData<>();
public void insertMyObject(MyObject object){
insertAysnc(object)
}
private void insertAysnc(final MyObject object){
new Thread(new Runnable() {
#Override
public void run() {
Long id=myDao.insertMyObject(object); //call Dao insert fcn
dbInsertId.postValue(id); //set the value of the livedata to the valued returned from the DB insert fcn
}
}).start();
}
public LiveData<Long> getDbInsertedId(){
return dbInsertId;
}
}
In the ViewModel define this fcn
public LiveData<Long> getDbInsertedId(){
return mRepository.getDbInsertedId();// call the repository getId fcn
}
in the onCreate of Activity/Fragment setup an observer on the LiveData
mViewModel= ViewModelProviders.of(this).get(MyViewModel.class);
mViewModel.getDbInsertedId().observe(this, new Observer<Long>() {
#Override
public void onChanged(Long aLong) {
// do something with the long value returned from the database
}
});

MVC - Two classes with same reference in Controller

I have been searching and can't seem to find the solution. I have two views, one controller, one model, and a Factory class. However the model is not important in this question.
I want to be able to use the same variable name for both classes depending on the user's choice. For example:
public class Controller {
public Controller(){
m = new Model();
}
//This method is called from Factory
/*Only one of these two will be called SetViewSwing() or SetViewKonsoll()*/
public void SetViewSwing(){
v = new View(this);
}
public void SetViewKonsoll(){
v = new ViewKonsoll(this);
}
}
And then further down in the controller class I could do something like:
v.updateGui(String text);
So depending on if SetViewSwing is called or SetViewKonsoll is called I want to assign the class to v, which can I then use later on in my controller class to execute methods in the viewclass the user selected
I don't know what you are going to do. But according to your question you want to access same view variable from controller so you need to follow this. You need a interface for this.
Declare a Common interface
public interface CommonView {
void updateGui(String text);
}
Then you have to implement this interface to both concrete view classes
public class View implements CommonView {
public View(ViewController viewController) {
}
#Override
public void updateGui(String text) {
System.out.println("Swing View");
}
}
And another class
public class ViewKonsoll implements CommonView {
public ViewKonsoll(ViewController viewController) {
}
#Override
public void updateGui(String text) {
System.out.println("KonsolView");
}
}
Then At Controller you can define like this
public class ViewController {
Model m;
CommonView v;
ViewController(){
m = new Model();
}
public void SetViewSwing(){
v = new View(this);
}
public void SetViewKonsoll(){
v = new ViewKonsoll(this);
}
}
Then you can set view from your controller or any places and call v.updateGui(String text) .

MVP pattern for retriving model

i'm developing an android app using MVP pattern.
I'd like to have different presenters, and each one implements getItems, that call a getAll static method of the associated model.
I thought to use generics, ended up like this:
public class BasePresenter<T> {
protected T mModel;
List getItems() {
mModel.getAll();
}
}
public class Presenter extends BasePresenter<Model> {
}
but i cannot access getAll methods using generics, because is not an Object's method.
Since for me would be dumb to write the same boring method getAll() for all presenter, changing only the model, is there there any way to accomplish that?
I'm asking because even in Google's official MVP guide, it use always the same method to retrive data, overriding it on each presenter, and i'm hoping that there is a better way.
I thought to "cast" the superclass mModel, but i don't know how and if it's possible to do, though.
Try this
public class BasePresenter<M extends BaseModel<M>> {
#Nullable
private M mModel;
#Nullable List<M> getItems() {
if (mModel != null) {
return mModel.getModelList();
}
return null;
}
}
And the BaseModel is
public abstract class BaseModel<M> {
private List<M> modelList;
public List<M> getModelList() {
return modelList;
}
public void setModelList(List<M> modelList) {
this.modelList = modelList;
}
}
Sub model
public class LoginModel extends BaseModel<LoginModel> {
#Override
public List<LoginModel> getModelList() {
return super.getModelList();
}
#Override
public void setModelList(List<LoginModel> modelList) {
super.setModelList(modelList);
}
}
And finally presenter is like this
public class LoginPresenter extends BasePresenter<LoginModel> {
//do your code
}
Hope it helps you.
Maybe this can help you:
List getItems(){
if(mModel instanceof TheSuperClassOrInterface){
return ((TheSuperClassOrInterface)mModel).getAll();
}else{
return null;
}
}

Java Guice Provider

I have a small problem which I can't figure out to save my life.
Basically I need to register classes anytime dynamically using guice and then loop through them all.
Lets say this is my class to register Strategies but these strategies can be added anytime through the application running.
// Strategy registration may happen anytime, this is just an example
strategyManager.register(ExampleStrategy1.class);
strategyManager.register(ExampleStrategy2.class);
StrategyImpl class
public class StrategyImpl implements Strategy {
#Override
public void register(Class<? extends StrategyDispatcher> strat) {
//Add this class into provider or create an instance for it and add it into guice but how?
}
#Override
public void dispatchStrategy() {
//Find all strategies and execute them
}
}
I've tried using a Provider but have no idea how i'd add the registered class into the provider and retrieve them all?
#Override
protected void configure() {
bind(Strategy.class).toProvider(StrategyProvider.class);
}
My provider class always gets the same instance
public class StrategyProvider implements Provider<StrategyDispatcher> {
public LogManager get() {
return new StrategyDispatcherImpl();
}
}
The strategies that I add extend the StrategyDispatcherImpl class so i could cast them?
I need to add multiple binds to a same instance but it needs to be done dynamically and not using the bind method in configure but another way then be able to find all these strategies and execute them.
If you truly need it to happen at "any time" during the application life cycle then Guice then I think you will need some sort of Guice-aware Factory. I.e.
public class TestStuff {
#Test
public void testDynamicCreation() {
Injector injector = Guice.createInjector();
StrategyManager manager = injector.getInstance(StrategyManager.class);
Hello hello = injector.getInstance(Hello.class);
manager.doStuff();
assertThat(hello.helloCalled, is(false));
manager.register(Hello.class); // DYNAMIC!!
manager.doStuff();
assertThat(hello.helloCalled, is(true));
}
}
interface Strategy {
void doStuff();
}
#Singleton
class Hello implements Strategy {
boolean helloCalled = false;
public void doStuff() {
helloCalled = true;
}
}
class StrategyManager {
private final Collection<Strategy> strategies = new ArrayList<>();
private final StrategyFactory factory;
#Inject
StrategyManager(StrategyFactory factory) {
this.factory = factory;
}
public void register(Class<? extends Strategy> strat) {
strategies.add(factory.create(strat));
}
public void doStuff() {
for (Strategy s : strategies) {
s.doStuff();
}
}
}
class StrategyFactory {
private final Injector injector;
#Inject
StrategyFactory(Injector injector) {
this.injector = injector;
}
public Strategy create(Class<? extends Strategy> clazz) {
return injector.getInstance(clazz);
}
}
If it is not "dynamic" after the initialization phase then you are after the "multibinder" I think.

Categories

Resources