Mocking findViewById response with Robolectric and Mockito - android

I have a custom ListView say CustomListView:
In a fragment there is:
<com.custom.CustomListView
android:id="#+id/custom_listview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
and in that fragment's source, I have
private CustomListView mCustomListView;
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mContext = getActivity();
mCustomListView = mContext.findViewById(R.id.custom_listview);
}
Then there is some method later:
public void doSomethingOnReceivingData(Data data) {
mCustomListView.someCustomMethod(data);
}
I want to write test for doSomethingOnReceivingData(Data) method.
I can not figure out how to mock the listview so that I can continue with the test (ArgumentCaptors and stuff)?

I would give to list field package local access and mock it in test directly. For our app it is already package accessible since we use Butterknife

#RunWith(MockitoJUnitRunner.class)
public class MainActivityFragmentTest {
#Mock
private CustomListView mCustomListView;
#InjectMocks
private MainActivityFragment fragment;
#Test
public void doSomethingOnReceivingData_callsCustomListView() {
final String data = "data";
fragment.doSomethingOnReceivingData(data);
verify(mCustomListView).someCustomMethod(eq(data));
}
}

Related

ViewModel does not get updated in the layout when setters are called from method other than onCreate()

I have a class named MyInfoModel that has some instance variables in it to store data about the user and a method to notify the observers of the class.
private final List<ObserverInterface> observers = new ArrayList<>();
private int id;
private String name;
private String imageURL;
// Getters, Setters, Register and Unregister Methods for Instance Variables
// Also All Setter Methods call the notifyObservers() method after setting the values
private void notifyObservers(){
Log.d(this.getClass().getName(), "Notifying All Observers");
for (ObserverInterface observer : observers){
observer.updateViews();
}
}
I have an interface ObserverInterface that has only one method void updateViews(); which is overridden by other classes that implement this interface.
I am using Retrofit to request some data which gets updated in a static object in the Constants class.
public class Constants {
public static MyInfoModel INFO_CONSTANTS = new MyInfoModel();
}
Also, I have a View Model MainActivityViewModel that extends ViewModel class and has variables, getters and setters to support the layout of the activity.
private int id;
private String name;
private String imageURL;
// Getters and Setters
I have used DataBinding in my layout like this
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="viewModel"
type="com.itsyourap.app.viewModel.MainActivityViewModel" />
</data>
<TextView <!-- Layout Tags -->
android:text="#{viewModel.Name}" />
Then I have the MainActivity where I want to wait for any change in the values of variables of Constants.INFO_CONSTANTS, so I implement the ObserverInterface interface and Override the updateViews() method like this:-
public class MainActivity extends AppCompatActivity implements ObserverInterface {
private MainViewModel mainViewModel;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding mainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
mainViewModel = new ViewModelProvider(this).get(MainActivityViewModel.class);
mainBinding.setViewModel(mainViewModel);
mainBinding.getViewModel().setName("Alpha Beta Gamma"); // Works
mainBinding.getViewModel().setImageURL("somelink.jpg"); // Works
}
public MainActivity(){
Constants.INFO_CONSTANTS.register(this);
}
#Override
public void updateViews() {
if (mainViewModel != null){
Log.d(this.getClass().getName(), "Updating Views");
mainViewModel.setName(Constants.INFO_CONSTANTS.getName()); // Does Not Work
mainViewModel.setImageURL(Constants.INFO_CONSTANTS.getImageURL()); // Does Not Work
}
}
}
The problem I am facing that the updateViews() is called successfully (according to LogCat) but the Layout isn't updated at the runtime. I have tried using LiveData and MutableLiveData but they do not seem to work as well. Also I tried using mainBinding.setLifecycleOwner(this); but it didn't work as well.
Also calling the setters from the onCreate() method works perfectly but does not work from the updateViews() method.
How should I make it work? I just want the values in the layout to change as soon as the values in the ViewModel gets changed.
Thanks in Advance
Calling setViewModel() after modifying the variables solves the issue
#Override
public void updateViews() {
if (mainViewModel != null){
Log.d(this.getClass().getName(), "Updating Views");
mainViewModel.setName(Constants.INFO_CONSTANTS.getName());
mainViewModel.setImageURL(Constants.INFO_CONSTANTS.getImageURL());
mainBinding.setViewModel(mainViewModel); // Solves The Problem
}
}

Mockito To Verify ArrayList Size

My Adapter has getItemCount method in which ArrayList returns size of items. How to i verify it with Mockito as i don't have any view in that method?
static class SongAdapterPresenter implements SortedSongSelectionContract.SongAdapterContract.Presenter {
.....
....
#Override
public int getItemCount() {
return songs != null ? songs.size() : 0;
}
}
My Mockito Class code
#RunWith(PowerMockRunner.class)
#PrepareForTest(Log.class)
public class SongAdapterPresenterTest {
private SortedSongSelectionPresenter.SongAdapterPresenter songAdapterPresenter;
#Mock
private SortedSongSelectionContract.SongAdapterContract.Adapter adapter;
#Mock
private SortedSongSelectionContract.Presenter presenter;
#Mock
private Song song;
private List<Song> songList;
#Before
public void setUp(){
songList=new ArrayList<>(1);
songList.add(song);
songAdapterPresenter=new SortedSongSelectionPresenter.SongAdapterPresenter(adapter,presenter);
}
#Test
public void testGetItemCount(){
songAdapterPresenter.getItemCount();
verify(songList.size()).equals(1);
}
#Test
public void testonBindView(){
}
}
When i run the testGetItemCount method i get org.mockito.exceptions.misusing.NotAMockException:
Any help would be greatly appreciated
The verify method of Mockito is used to interact with a mock, and make sure it was used as intended. For example, you could write verify(song).something() to make sure that the something method was called on the song mock.
In your case it looks like you are just looking for a way to verify (assert) the returned value. As this has nothing to do with your song mock, you should use a framework like Hamcrest with its assertThat method:
#Test
public void testGetItemCount() {
int count = songAdapterPresenter.getItemCount();
assertThat(count, is(1));
}
You can write assertions in any way you feel comfortable. As you're not interacting with a mock (song in your case), this is not the place to use Mockito.

Mock PreferenceManager with Mockito

I am new in Android unit testing and I want to add some unit tests in an existing project. I am using the MVP design architecture. Inside my presenter, I have a call to PreferenceManager in order to get the default SharedPrefences but it always returns null. I have followed some tutorials and advices across stackoverflow on how to mock PreferenceManager and SharedPreferences but I can't make it work. This is my presenter class
#RunWith(MockitoJUnitRunner.class)
public class SettingsPresenterTest {
#Mock
private SettingsView mView;
#Mock
private LocalConfiguration conf;
private SettingsPresenter mPresenter;
public SettingsPresenterTest() {
super();
}
#Before
public void startUp() throws Exception {
MockitoAnnotations.initMocks(LocalConfiguration.class);
MockitoAnnotations.initMocks(PreferenceManager.class);
mPresenter = new SettingsPresenter(mView);
final SharedPreferences sharedPrefs =
Mockito.mock(SharedPreferences.class);
final Context context = Mockito.mock(Context.class);
Mockito.when(PreferenceManager.getDefaultSharedPreferences(context)).
thenReturn(sharedPrefs);
}
#Test
public void notificationsEnabledClicked() throws Exception {
boolean notifsEnabled = false;
mPresenter.notificationsEnabledClicked(notifsEnabled);
Mockito.verify(mView).setNotificationsView(notifsEnabled);
}
}
and here is the method where the SharedPreferences are returned null
public class LocalConfiguration {
public TerritoryDto getLastSavedTerritory() {
SharedPreferences preferences =
PreferenceManager.getDefaultSharedPreferences(H.getContext());
String terrritoryString = preferences.getString(SAVED_TERRITORY,
null);
return
SerializerHelper.getInstance().deserialize(terrritoryString,
TerritoryDto.class);
}
}
Could you give me some guidelines on how to resolve this error?
Instead of directly referring to Android SDK, abstract that out from your presenter logics. What this means is, that instead of performing PreferenceManager.getDefaultSharedPreferences(), create an abstraction and ask for territoryString from your abstraction.
What will this give to you, is that your presenter won't know about the precense of neither PreferenceManager nor SharedPreferences, which are from Android SDK, thus you would have enough seams to perform pure unit testing.
Having said this, let's implement the abstractions. Having declared following interface:
public interface Storage {
#Nullable
String getSavedTerritory();
}
To which the concrete implementation would be:
public SharedPrefsStorage implements Storage {
private final SharedPreferences prefs;
public SharedPrefsStorage(Context context) {
prefs = PreferenceManager.getDefaultSharedPreferences();
}
#Nullable
#Override
public String getSavedTerritory() {
return prefs.getString(SAVED_TERRITORY, null);
}
}
Then your presenter would become something like this:
public class LocalConfiguration {
final Storage storage;
public LocalConfiguration(Storage storage) {
this.storage = storage;
}
public TerritoryDto getLastSavedTerritory() {
final String territory = storage.getSavedTerritory();
return SerializerHelper.getInstance().deserialize(territory, TerritoryDto.class);
}
}
This would give you a seam to perform pure unit testing:
#Test
void someTest() {
when(storage.getSavedTerritory()).thenReturn("New York");
...
}
No need to worry about mocking PreferenceManager anymore.

Testing a fragment (Android), mocking getActivity() call

I want to test a Fragment with AndroidTest cases and Mockito (I am using mockito for other test cases).
I´ve done this before with my own code (coded in a different way) but in this case, I am testing a Fragment and I want to mock this call: final PackageManager packageManager = getActivity().getPackageManager();
I will put you here part of the TestClass, and part of the Fragment that I want to test.
Thanks in advance for your ideas or suggestions.
public class MyFragmentTest extends
ActivityInstrumentationTestCase2<MyActivity>{
MyFragment myFragment;
public MyFragmentTest () {
super(MyActivity.class);
}
#Override
public void setUp() throws Exception {
super.setUp();
// This have to be done because of some issues with dexmaker
System.setProperty("dexmaker.dexcache", "/sdcard");
// This have to be done because of the sharedUserId problem
Thread.currentThread().setContextClassLoader(
getClass().getClassLoader());
myFragment = new MyFragment() {
//I can override methods here
};
}
public void testMyMethod() throws Exception {
myFragment.methodThatIWantToTest();
}
}
/************ CLASS THAT I WANT TO TEST *********/
public class MyFragment extends Fragment{
public void methodThatIWantToTest(){
/*..... more lines */
final PackageManager packageManager = getActivity().getPackageManager();
/*..... more lines ...*/
}
}
I have employed this hack:
// Needed because Fragment.mActivity is package-private
package android.support.v4.app;
public class FragmentInjector {
public static void injectActivity(Fragment fragment, FragmentActivity fragmentActivity) {
fragment.mActivity = fragmentActivity;
}
}
Alternatively you could employ reflection to change the value of fragment.mActivity. I don't know of any other way.

What is wrong with RoboGuice

I want to create a singleton object using RoboGuice but I get null exception. I don't know what is wrong with my codes.
#Singleton
public class SessionService {
private static Session session;
public Session getSession() {
if (session == null){
session = new Session();
}
return session;
}
}
--
public class ChannelManager {
#Inject SessionService sessionService;
public String getName(){
return sessionService.getSession().getName();
}
}
public class MainActivity extends RoboActivity{
#InjectView(R.id.button1) Button btn;
#Inject SessionService a;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
a.getSession().setName("dsadas");
Log.i("A","NEW: "+ a.getSession().getName());
Log.i("A","NEW NAME: "+ new ChannelManager().getName());
}
I get null exception on "new ChannelManager().getName()" line. What's wrong with that?
Thanks in advance.
When you do new ChannelManager(), you are not using Guice injection, so your injected fields are null.
To inject your ChannelManager, either use the #Inject annotation or use the following code to create your instance:
ChannelManager myChannelManager = RoboGuice.getInjector(this).getInstance(ChannelManager.class);
Also consider if there is necessity to use 'new' operator to create e Object. This always implicate some problems especially in (unit)tests.

Categories

Resources