Mocking getActionBar() returns null - java

I'm testing an activity, and I want to mock getActionBar(). I've tried doing it the usual way, that is
ActionBar mActionBar;
#Before
public void setup() {
initMocks(this);
mActionBar = Mockito.mock(ActionBar.class);
}
#Test
public void someTest(){
when(activity.getActionBar()).thenReturn(mActionBar);
}
But that doesn't seem to do anything, because I still get an NPE, when I try using the action bar in the activity after getActionBar().

You are probably calling other methods on the mocked ActionBar in the code you are testing, like for example:
Tab tab = actionBar.getSelectedTab()
This returns null. Using tab then will result in a NPE.
This means you will need to mock more, for example:
when(actionBar.getSelectedTab()).thenReturn(mock(Tab.class));
Now the above example will return a mocked Tab.

I assume you're setting Activity as it is written in Robolectric tutorial. There is no simple way to mock real activity method inside of activity code itself.
I would suggest you create TestMyActivity that extends your activity and lives only in test sources. Then you can override getActionBar() (probably getSupportActionBar())).
public class TestMyActivity extends MyActivity {
#Override
ActionBar getActionBar() {
return mockedActionBar;
}
}

Related

Getting null value in fragment spinner

I am trying to use filter in Fragment and implementing the dialog fragment.
This is the class that I am using
public class HomeFragment extends Fragment implements
FilterDialogFragment.FilterListener,
PostAdapter2.OnPostSelectedListener{ detail code }
this the dialogfragment based class for spinner choosing options
public class FilterDialogFragment extends DialogFragment
this method is called upon clicking the filter button, which pops up dialog for spinner options of the filter
Declared
private FilterDialogFragment mFilterDialog;
in onCreateView
mFilterDialog = new FilterDialogFragment();
Method to call
public void onFilterClicked(){
mFilterDialog.show(getSupportFragmentManager(), FilterDialogFragment.TAG);
}
after this upon selecting the spinner option and clicking apply this method is called in which mFilterListener is null which should not be the case
public interface FilterListener {
void onFilter(Filters filters);
}
FilterListener mFilterListener;
public void onSearchClicked() {
Log.d("Message", String.valueOf(mFilterListener));
if (mFilterListener != null) {
Log.d("Message", "New 55555");
mFilterListener.onFilter(getFilters());
}
dismiss();
}
please assist me to solve this problem. if anymore details are required please let me know
attach method in FilterDialogFragement
public void onAttach(Context context) {
super.onAttach(context);
Log.d("Message", "New 6666666");
Log.d("Message", String.valueOf(mFilterListener));
if (context instanceof FilterListener) {
// Log.d("Message", String.valueOf(mFilterListener));
mFilterListener = (FilterListener) context;
}
}
You are attempting to mimic this library implementation: Friendly Eats.
However, you do not copy it wholesale, mainly in that you choose to use HomeFragment which implements FilterDialogFragment.FilterListener to launch FilterDialogFragment, rather than the library's MainActivity. This is the cause of your null pointer.
This is due to how getSupportFragmentManager() works. If you look at Android's documentation for this, you will see it says
Return the FragmentManager for interacting with fragments associated with this activity. (My Bolding)
When you call mFilterDialog.show(getSupportFragmentManager(), FilterDialogFragment.TAG); inside HomeFragment, you are actually calling whatever Activity that is the parent of HomeFragment to launch the new FilterDialogFragment. You could double check this by checking if, in onAttach(Context context) inside HomeFragment, if context instanceof HomeFragment. I do not think it will return true.
To solve this, and without changing your use of HomeFragment, you could simply pass an instance of HomeFragment itself, or a separate implementation of FilterDialogFragment.FilterListener (which I'd prefer if you do not need to use anything from HomeFragment other than the listener) to your FilterDialogFragment instance on creation or before you launch it.
For example, you could create a public setter like so:
private FilterListener mFilterListener;
public void setFilterListener(FilterListener filterListener){
mFilterListener = filterListener;
}
and then in your HomeFragment onCreateView(), you do this:
mFilterDialog = new FilterDialogFragment();
//Or preferably, an anonymous/named implementing instance of the interface only.
mFilterDialog.setFilterListener(this);
Doing so would not rely on the Android framework to provide the initialisation of your field, and does not require you to either change your Activity or HomeFragment you are currently using.
I assume, that u didnt set the listener in a mFilterDialog, so thats why its null

Call notifyDataSetChanged() within Fragment

I'm using ViewPager in MainActivity.java with five Fragments.
Now I've got a problem.
Running notifyDataSetChanged() works fine like this:
viewpager.getAdapter().notifyDataSetChanged();
But how can I run notifyDataSetChanged() from a Fragment?
I have no idea.
What I've tried
Making ViewPager static and then run
MainActivity.this.viewpager.getAdapter().notifyDataSetChanged()
doesn't work.
Add a method to your MainActivity like
public void dataChanged() {
viewpager.getAdapter().notifyDataSetChanged();
}
and from your fragments call it with
((MainActivity) getActivity()).dataChanged();
You can do this by various methods:
The easiest one - declare a public method in MainActivity.java as following:
public void onDataSetChanged(){
if(viewPager != null) {
viewPager.getAdapter().notifyDataSetChanged()
}
}
And then call it from the fragment like this:
((MainActivity) getActivity()). onDataSetChanged();
Using Interface: You can create your own interface as :
public interface OnDataSetChangeListener {
void onDataSetChanged();
}
Declare and define an instance of it in MainActivity.java and then set it to the instance of your fragment. Then call it from fragment as:
onDataSetChangeListener.onDataSetChanged();
Using EventBuses.
And many more.

Unable to find Textview in testing

I am unable to find Textview by id in test. What I do wrong?
private MyActivity myActivity;
#Before
public void setUp() throws Exception {
myActivity= Mockito.mock(MyActivity .class);
}
Test:
#Test
public void testFindView() throws Exception {
System.out.println(myActivity); // This is not null
this.myActivity.setContentView(R.layout.container);
TextView viewText = (TextView) this.myActivity.findViewById(R.id.container_text);
System.out.println(viewText ); // This is null
}
Calling Mockito.mock() doesn't create a real instance, but only an artificial one. It's main purpose is to keep unit tests away from any external dependencies and track interactions with an object.
So when you call this.myActivity.setContentView(R.layout.container); nothing really happens, because mocked myActivity doesn't have the insides of a regular MyActivity - you're only calling a stub method that you have not ordered to do anything.
So you need to create a real instance of MyActivity if you want to test how it works. You can also play with Spy objects if you still want to track interactions (you can check them out here)

How to get a view from within Espresso to pass into an IdlingResource?

I essentially have a custom IdlingResource that takes a View a constructor argument. I can't find anywhere that really talks about how to implement it.
I'm trying to use this answer: https://stackoverflow.com/a/32763454/1193321
As you can see, it takes a ViewPager, but when I'm registering the IdlingResource in my test class, I'm not sure how I can get my view.
I've tried findViewById() and I've tried getting the currently running activity and then calling findViewById() on that, with no luck.
Anyone know what to do in this scenario?
Figured it out. To get the view to pass into an idling resource, all you have to do is take the member variable of your ActivityTestRule
For example:
#Rule
public ActivityTestRule<MainActivity> activityTestRule = new ActivityTestRule<>(
MainActivity.class);
and then just call getActivity().findViewById(R.id.viewId)
So the end result is:
activityTestRule.getActivity().findViewById(R.id.viewId);
The accepted answer works as long as a test is running in the same activity. However, if the test navigates to another activity activityTestRule.getActivity() will return the wrong activity (the first one). To address this, one can create a helper method returning an actual activity:
public Activity getCurrentActivity() {
final Activity[] currentActivity = new Activity[1];
InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
#Override
public void run() {
Collection<Activity> allActivities = ActivityLifecycleMonitorRegistry.getInstance()
.getActivitiesInStage(Stage.RESUMED);
if (!allActivities.isEmpty()) {
currentActivity[0] = allActivities.iterator().next();
}
}
});
return currentActivity[0];
}
And then it could be used as the following:
Activity currentActivity = getCurrentActivity();
if (currentActivity != null) {
currentActivity.findViewById(R.id.viewId);
}
If you are using ActivityScenarioRule from androidx.test.ext.junit.rules (since ActivityTestRule "will be deprecated and eventually removed from library in the future"), you can get your Activity instance and call findViewById method:
import androidx.test.ext.junit.rules.activityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
#RunWith(AndroidJUnit4::class) {
#get: Rule
var testRule = activityScenarioRule<MainActivity>()
#Test
fun mainTestCase() {
testRule.scenario.onActivity { activity ->
val view = activity.findViewById<YourView>(R.id.view)
}
}
}
I haven't already used IdilingResources in Espresso, but did you saw these articles:
Espresso: Custom Idling Resource by Chiuki
Wait for it...a deep dive into Espresso's Idling Resources
Also please check official Android Docs: Idling Resources (reference)
To answer your question,
the best way to do it is passing in an instance of one of the Views into the class's constructor. Check: Calling findViewById() from outside an activity
another way is getting view by context. Check android - How to get view from context?
Here's an exmple taken from a link above:
Starting with a context, the root view of the
associated activity can be had by
View rootView = ((Activity)_context).Window.DecorView.FindViewById(Android.Resource.Id.Content);
In Raw Android it'd look something like:
View rootView = ((Activity)mContext).getWindow().getDecorView().findViewById(android.R.id.content)
Then simply call the findViewById on this
View v = rootView.findViewById(R.id.your_view_id);
This might be also useful: How to call getResources() from a class which has no context?
Hope it help

How to spy on Activity when using Robolectric

I am new on Android and I am playing around with Robolectric for my unit tests.
I am facing the following problem.
I have an activity I want to test.
MainActivity.java
public class MainActivity extends ActionBarActivity
implements NavigationDrawerFragment.NavigationDrawerCallbacks {
private NavigationDrawerFragment mNavigationDrawerFragment;
#Override
protected void onCreate (Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mNavigationDrawerFragment = (NavigationDrawerFragment)
getSupportFragmentManager().findFragmentById(R.id.navigation_drawer);
mNavigationDrawerFragment.setUp(
R.id.navigation_drawer,
(DrawerLayout) findViewById(R.id.drawer_layout));
}
#Override
public void onNavigationDrawerItemSelected (int position) {
...
}
}
This is the test class:
#RunWith(RobolectricGradleTestRunner.class)
#Config(constants = BuildConfig.class)
public class MainActivityTests {
private ActivityController<MainActivity> controller;
private MainActivity activity;
private MainActivity spy;
#Test
public void onCreate_shouldStartNavigationDrawerFragment () {
controller = Robolectric.buildActivity(MainActivity.class);
activity = controller.get();
assertThat(activity).isNotNull();
spy = spy(activity);
spy.onCreate(null);
verify(spy).onCreate(null);
}
}
But I am getting the following exception:
java.lang.IllegalStateException: System services not available to Activities before onCreate() at line spy.onCreate(null).
I have been googling for hours and I have tried several workarounds (blindly) without any success. May please anyone guide me?
Here's what did the trick for me. I use attach() before getting an activity to spy on. Tested with Robolectric 3.0
private MainActivity spyActivity;
#Before
public void setUp(){
MainActivity activity = Robolectric.buildActivity(MainActivity.class).attach().get();
spyActivity = spy(activity);
spyActivity.onCreate(null);
}
You should be driving the activity lifecycle through Robolectric.
See: http://robolectric.org/activity-lifecycle/
So for your case you could do:
controller = Robolectric.buildActivity(MainActivity.class);
activity = controller.get();
assertThat(activity).isNotNull();
spy = spy(activity);
controller.create();
Note: it usually doesn't make sense to spy on the activity lifecycle when testing with Robolectric, since you're the one driving it, so you're only testing that your own method calls executed.
If interested in using exactly the same controller, and work with a spy of the activity, you could modify the inner class of the controller via Reflection, check this method:
public static <T extends Activity> T getSpy(ActivityController<T> activityController) {
T spy = spy(activityController.get());
ReflectionHelpers.setField(activityController, "component", spy);
return spy;
}
The ReflectionHelper is available in Robolectric (tested on Robolectric 4.2). Then it is initialized like this:
controller = Robolectric.buildActivity(MainActivity.class);
activity = getSpy(controller.get());
Hope this helps.
It means that you have to first call the onCreate() method. It has to be the very first called method.

Categories